Почему я получаю сообщение «объявление for не относится к классу, шаблону класса или частичной специализации шаблона класса» в Clang, а не в GCC

В настоящее время у меня есть этот блок кода, который не выдает ошибку компилятора в GCC, но выдает эту ошибку компилятора в clang:

ошибка: вложенный спецификатор имени 'MyEnum::' для объявления не относится к классу, шаблону класса или частичной специализации шаблона класса.

На самом деле я не слишком уверен, почему это так. В приведенном ниже примере кода я предложил некоторые потенциальные решения, позволяющие избежать возникновения ошибки, и мой конкретный вариант использования. Вот ссылка на скомпилированный пример: https://godbolt.org/z/57aMreM3K

#include <stdexcept>

enum class MyEnum : int {
        MIN                  = 0,
        MAX                  = 1
};

class TestClass {
    public:
        explicit TestClass(const MyEnum type) { 
            throw std::runtime_error("Runtime Error");
        }
};

int main() {
    try {
        TestClass(MyEnum::MIN);
    } catch(...) { }
    
    // My usecase was to use google test to make sure there's no exception on construction
    // EXCEPT_NO_THROW(TestClass(MyEnum::MIN));

    // Potential solutions that compile for both GCC/Clang:
    // EXCEPT_NO_THROW(auto variable = TestClass(MyEnum::MIN));
    // EXCEPT_NO_THROW(TestClass((MyEnum)MyEnum::MIN));
}

Компилятор рассматривает TestClass(MyEnum::MIN); как TestClass MyEnum::MIN;. То есть он думает, что вы пытаетесь определить переменную с именем MyEnum::MIN, которая недопустима при перечислении. Используйте фигурные скобки {} вместо круглых, чтобы решить задачу: TestClass{MyEnum::MIN};

Some programmer dude 17.07.2024 20:57

@Someprogrammerdude: Или используйте дополнительные круглые скобки ((TestClass(MyEnum::MIN));), чтобы избежать изменения значения, как это иногда делают фигурные скобки (но не в этом случае).

Davis Herring 17.07.2024 22:41
Стоит ли изучать 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 называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
3
81
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

Кланг здесь прав.

В базовой грамматике C++ для вашего утверждения имеется двусмысленность.

    TestClass(MyEnum::MIN);

Зная только то, что TestClass является допустимым именем типа, но не выполняя никакого поиска, кроме этого, это может быть либо:

  1. Оператор выражения, который состоит из явного выражения приведения функционального стиля, которое преобразует результат идентификатора-выражения MyEnum::MIN в TestClass.

  2. Объявление со спецификатором типа TestClass, без какого-либо инициализатора и с квалифицированным идентификатором-декларатором в скобках MyEnum::MIN. Обратите внимание, что в C и C++ деклараторы всегда можно заключить в круглые скобки без изменения смысла, например. int (x) = 5; — допустимое объявление переменной x типа int, инициализированной как 5.

Вам нужен первый смысл. Однако двусмысленность всегда разрешается в пользу объявления, основанного исключительно на синтаксической форме оператора и на том, являются ли составляющие допустимым именем типа. См. [stm.ambig] в стандарте (проекте) для получения точных правил.

В частности, интерпретация объявления синтаксически возможна, но семантически некорректна, поскольку вы не можете (пере)объявить перечислитель MyEnum::MIN.

Но поскольку семантика не учитывается при устранении неоднозначности, должна быть выбрана интерпретация как декларация, и программа оказывается неправильной как следствие неправильной формы декларации.

Тот факт, что GCC не диагностирует проблему, является открытой ошибкой, см. https://gcc.gnu.org/bugzilla/show_bug.cgi?id=62116.


Простой способ предотвратить возникновение неоднозначностей такого рода — использовать фигурные скобки вместо круглых скобок для явного приведения в функциональном стиле:

TestClass{MyEnum::MIN};

В зависимости от того, что вы хотите протестировать, это может оказаться не решением, поскольку может изменить смысл выражения.

Некоторые другие возможности устранения неоднозначности в качестве оператора выражения, которые не влияют на смысл вашего выражения:

 (void)TestClass(MyEnum::MIN);
 static_cast<void>(TestClass(MyEnum::MIN));
 (TestClass(MyEnum::MIN));
 void(), TestClass(MyEnum::MIN);
 TestClass(MyEnum::MIN), void();

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