Странная ошибка компиляции GCC (простой пример включен) - PullRequest
19 голосов
/ 16 января 2012

это довольно простой вопрос, но я не понимаю, почему приведенный ниже код не компилируется в GCC 4.6.1. Он компилируется на VS 2008 с SP1:

#include <iostream>

class MyClass
{
public:
    const static int MinValue = -1000;
    const static int MaxValue = 1000;
};

void printValue(int i)
{
  std::cout << i << std::endl;
}

int main(int argc, char** argv)
{
  printValue(MyClass::MinValue);
  printValue(MyClass::MaxValue);
  printValue(argc < 42 ? MyClass::MinValue : MyClass::MaxValue); //This line gives the error
}

GCC говорит:

david@David-Laptop:~/temp$ g++ test.cpp
/tmp/ccN2b95G.o: In function `main':
test.cpp:(.text+0x54): undefined reference to `MyClass::MinValue'
test.cpp:(.text+0x5c): undefined reference to `MyClass::MaxValue'
collect2: ld returned 1 exit status

Однако, если я уберу третий вызов 'printValue', он будет правильно скомпилирован и запущен. Так что это как-то связано с '?' оператор ... не допустимо использовать это так? Кроме того, если я заменю «argc <42» на «true» или «false», он также будет работать нормально. </p>

Есть идеи?!

Ответы [ 3 ]

18 голосов
/ 16 января 2012

Согласно «Правилу одного определения» переменная должна иметь ровно одно определение, если используется odr-used . Это определяется стандартом C ++ 11:

3.2 / 2 Переменная или не перегруженная функция, имя которой появляется в качестве потенциально оцениваемого выражения, используется odr, если только она не является объектом, удовлетворяющим требованиям для появления в константном выражении, а преобразование lvalue-to-rvalue равно немедленно применяется.

и само ODR:

3.2 / 3 Каждая программа должна содержать ровно одно определение каждой не встроенной функции или переменной, которая используется в этой программе в виде odr; Диагностика не требуется.

Как аргументы вызова функции, они не odr-used : они являются константами со значением, указанным в их объявлении, и поэтому могут появляться в константном выражении; и они передаются по значению, и поэтому немедленно преобразуются в значения.

Это не тот случай, когда они используются в условном выражении. Поскольку оба являются lvalue, относящимися к одному и тому же типу, результатом условного выражения будет lvalue, согласно одному из довольно сложных правил, определяющих условный оператор:

5.16 / 4 Если второй и третий операнды являются значениями одной и той же категории значений и имеют один и тот же тип, результат будет иметь тип и категорию значений.

(Это правило допускает выражения типа (a?b:c)=d.)

Таким образом, сами константы не сразу преобразуются в значения r, и условное выражение не может появляться в постоянном выражении из-за условия времени выполнения; следовательно, они используются odr, и поэтому нуждаются в определении.

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

const int MyClass::MinValue;
const int MyClass::MaxValue;
7 голосов
/ 16 января 2012

Вам нужно определить ваши статические члены вне объявления класса:

class MyClass
{
public:
    const static int MinValue;
    const static int MaxValue;
};


//implementation file
const int MyClass::MinValue = -1000;
const int MyClass::MaxValue = 1000;
0 голосов
/ 16 января 2012

Я собираюсь выйти на конечность и сказать, что это, вероятно, ошибка компилятора с версией, которую вы используете. Как отметил AzzA в своем комментарии, gcc-4.5.1, похоже, прекрасно его строит, как и gcc-4.3.4 . Я также только что проверил это с CLang 2.9, и он также принимает код.

В качестве альтернативы, вы можете определить ваши минимальные / максимальные значения с помощью перечислений, а не статических констант. Это позволит сэкономить при наборе:

#include <iostream>

class MyClass
{
public:
    enum { MinValue = -1000, MaxValue = 1000 };
};

void printValue(const int i)
{
  std::cout << i << std::endl;
}

int main(int argc, char** argv)
{
  printValue(MyClass::MinValue);
  printValue(MyClass::MaxValue);
  printValue(argc < 42 ? MyClass::MinValue : MyClass::MaxValue);
}
...