Перечисление C ++ неправильно распознано компилятором - PullRequest
4 голосов
/ 25 марта 2009

Может кто-нибудь объяснить, почему следующий код не компилируется (на g ++ (GCC) 3.2.3 20030502 (Red Hat Linux 3.2.3-49))?

struct X {
public:
   enum State { A, B, C };

   X(State s) {}
};

int main()
{
   X(X::A);
}

Я получаю сообщение:

jjj.cpp: в функции 'int main ()':
jjj.cpp: 10: «X X :: A» не является статическим членом «struct X»
jjj.cpp: 10: нет соответствующей функции для вызова 'X :: X ()'
jjj.cpp: 1: кандидаты: X :: X (const X &)
jjj.cpp: 5: X :: X (X :: State) `

Это плохой код или ошибка компилятора?

Проблема решена Нилом + Конрадом. См. Комментарии к ответу Нила ниже.

Ответы [ 6 ]

10 голосов
/ 25 марта 2009

Вы забыли имя переменной в своем определении:

int main()
{
   X my_x(X::A);
}

Ваш код сбивает с толку компилятор, потому что синтаксически он не может отличить это от объявления функции (возвращая X и передавая X::A в качестве аргумента). В случае сомнений компилятор C ++ всегда устраняет неоднозначность в пользу объявления.

Решение состоит в том, чтобы ввести избыточные скобки вокруг X, поскольку компилятор запрещает скобки вокруг типов (в отличие от вызовов constructo и т. Д.):

(X(X::A));
8 голосов
/ 25 марта 2009
X(X::A);

рассматривается как объявление функции. Если вы действительно хотите этот код, используйте:

(X)(X::A);
1 голос
/ 25 марта 2009

Просто чтобы было предельно ясно, что происходит. Посмотрите на этот пример

int main() {
    float a = 0;
    {
        int(a); // no-op?
        a = 1;
    }
    cout << a;
}

Что это будет выводить? Ну, это выведет 0. int(a) выше может быть проанализирован двумя различными способами:

  • Приведите к int и отбросьте результат
  • Объявите переменную с именем a. Но игнорируйте скобки вокруг идентификатора.

Компилятор, когда возникает такая ситуация, когда в операторе используется приведение стиля функции и он также выглядит как объявление, всегда будет принимать его как объявление. Когда это не может быть синтаксически объявлением (компилятор будет смотреть на всю строку, чтобы определить это), оно будет считаться выражением. Таким образом, мы присваиваем внутренний a выше, оставляя внешний a равным нулю.

Теперь ваш случай именно такой. Вы пытаетесь (случайно) объявить идентификатор с именем A в классе с именем X:

X (X::A); // parsed as X X::A;

Затем компилятор начинает жаловаться на не объявленный конструктор по умолчанию, потому что статический, как он предполагает, создается по умолчанию. Но даже если у вас был конструктор по умолчанию для X, это, конечно, все равно неправильно, потому что ни A не является статическим членом X, ни статическим элементом X нельзя определить / объявить в области видимости блока.

Вы можете сделать его не похожим на объявление, выполнив несколько действий. Во-первых, вы можете ограничить выражение целиком, что больше не будет выглядеть как объявление. Или просто проставьте тот тип, который приведен к. Обе эти неоднозначности были упомянуты в других ответах:

(X(X::A)); (X)(X::A)

Существует похожая, но явная неоднозначность, когда вы пытаетесь фактически объявить объект. Посмотрите на этот пример:

int main() {
    float a = 0;
    int b(int(a)); // object or function?
}

Поскольку int(a) может быть как объявлением параметра с именем a, так и явным преобразованием (приведением) переменной с плавающей запятой в тип int, компилятор снова решает, что это объявление. Таким образом, мы случайно объявили функцию с именем b, которая принимает целочисленный аргумент и возвращает целое число. Существует несколько возможностей для устранения неоднозначности, основанных на устранении неоднозначности выше:

int b((int(a))); int b((int)a);
0 голосов
/ 25 марта 2009

Конечно, вы можете просто сделать что-то вроде этого:

int main()
{
    // code
    {
    X temp(X::A);
    }
    // more code
}

Это было бы более читабельным и в основном иметь тот же эффект.

0 голосов
/ 25 марта 2009

У меня работает любая из этих двух строк:

X obj(X::A);
X obj2 = X(X::A);

Как указывает Нил Баттерворт, X(X::A) рассматривается как объявление функции. Если вы действительно хотите анонимный объект, (X)(X::A) создаст объект X и немедленно удалит его.

0 голосов
/ 25 марта 2009

Вы должны объявить объект как

X x(X::A);

Ошибка в вашем коде.

...