Могут ли `::` и `*`, образующие тип указателя-члена, происходить из разных расширений макроса или они должны появляться как один токен?

Рассмотрим этот код:

#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?

Просто любопытно: какую проблему должен решить этот макрос?

Marek R 15.05.2024 13:49

Ну... github.com/Perl/perl5/issues/18780

StoryTeller - Unslander Monica 15.05.2024 14:00

@Eljay - В нем вполне может быть место

StoryTeller - Unslander Monica 15.05.2024 14:01

Похоже на ошибку clang.

user12002570 15.05.2024 14:08

@MarekR Я создаю макрос, который делает что-то вроде этого: FOO(A::B::, c) -> static_cast<R (A::B::*)(...)>(&A::B::c), что в конечном итоге необходимо для генерации привязок pybind11.

HolyBlackCat 15.05.2024 14:09

Я думаю, что кто-то из clang немного придирчив к предупреждениям.

molbdnilo 15.05.2024 14:10

@StoryTeller-UnslanderMonica Да, видел эту тему, но не нашел там ничего полезного.

HolyBlackCat 15.05.2024 14:11

@HolyBlackCat — Мой вывод заключается в том, что это предупреждение Clang предназначено для обеспечения соблюдения конкретной идеи о ясности кода.

StoryTeller - Unslander Monica 15.05.2024 14:14

@StoryTeller-UnslanderMonica У меня 30 000 проблем, и составной токен, разделенный макросом, не является одним

Yakk - Adam Nevraumont 15.05.2024 15:33
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
9
97
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

::* — это не один 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 находят это запутанным и, возможно, непреднамеренным, когда :: и * берутся из разных расширений макросов или разделяются пробелами. Действительно, я уверен, что это сбивает с толку некоторых людей, но я уверен, что для этого есть и законные причины, поэтому я бы рекомендовал отключить это предупреждение.

Другие вопросы по теме