Рассмотрим этот код:
#define FOO A::
struct A
{
int x;
};
int FOO *ptr = &A::x;
Clang (18.1.0 без флагов) выдает предупреждение:<source>:8:5: warning: '::' and '*' tokens forming pointer to member type appear in different macro expansion contexts [-Wcompound-token-split-by-macro]
MSVC и GCC принимают код как есть.
Я думал, он хочет, чтобы я склеил :: и * вместе, но это вызывает серьезную ошибку во всех трех компиляторах:
#define CAT(x, y) CAT_(x, y)
#define CAT_(x, y) x##y
int CAT(FOO,*) ptr = &A::x;
error: pasting formed '::*', an invalid preprocessing token
Законен ли первый фрагмент или нет согласно стандарту? Если да, есть ли способ отключить это предупреждение, кроме #pragma?
Ну... github.com/Perl/perl5/issues/18780
@Eljay - В нем вполне может быть место
Похоже на ошибку clang.
@MarekR Я создаю макрос, который делает что-то вроде этого: FOO(A::B::, c) -> static_cast<R (A::B::*)(...)>(&A::B::c), что в конечном итоге необходимо для генерации привязок pybind11.
Я думаю, что кто-то из clang немного придирчив к предупреждениям.
@StoryTeller-UnslanderMonica Да, видел эту тему, но не нашел там ничего полезного.
@HolyBlackCat — Мой вывод заключается в том, что это предупреждение Clang предназначено для обеспечения соблюдения конкретной идеи о ясности кода.
@StoryTeller-UnslanderMonica У меня 30 000 проблем, и составной токен, разделенный макросом, не является одним





::* — это не один pp-токен. Операторы, являющиеся pp-токенами, приведены в [lex.operators]/1 . Там :: и * — это два отдельных токена, и для ::* нет записи. Таким образом, незаконно пытаться объединить два pp-токена для получения ::*, поскольку оператор вставки токена должен создать в качестве результата один pp-токен ( [cpp.concat]/3).
Конечно, два отдельных токена могут быть получены из двух разных макрорасширений; это ничем не отличается от чего-то вроде
#define foo const
#define bar int
foo bar x; // const int x;
const и int — это два отдельных токена, которые, когда они встречаются рядом друг с другом в этом контексте, подразумевают тип const int. Аналогично, когда в некоторых контекстах за токеном :: следует токен *, вы объявляете указатель на член или называете тип указателя на член.
Действительно, :: и * можно разделить пробелами, не меняя их значения. Это справедливо для любых отдельных токенов в языке после завершения предварительной обработки; см. [lex.phases]/1.7.
В стандарте C++ нет такого понятия, как «составной токен». Разработчики Clang это придумали. Вы можете попробовать поискать «составной токен» в https://eel.is/c++draft/full и вы ничего не найдете. Кажется, разработчики Clang находят это запутанным и, возможно, непреднамеренным, когда :: и * берутся из разных расширений макросов или разделяются пробелами. Действительно, я уверен, что это сбивает с толку некоторых людей, но я уверен, что для этого есть и законные причины, поэтому я бы рекомендовал отключить это предупреждение.
Просто любопытно: какую проблему должен решить этот макрос?