Странная ошибка компиляции GCC (простой пример включен)
Это довольно простой вопрос, но я не понимаю, почему приведенный ниже код не компилируется на GCC 4.6.1. Он компилируется на VS 2008 с пакетом обновления 1:
#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 говорит:
[email protected]:~/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 'с "истинным" или "ложным", он также отлично работает.
Любые идеи?!
Ответы
Ответ 1
Вам нужно определить ваши статические члены вне объявления класса:
class MyClass
{
public:
const static int MinValue;
const static int MaxValue;
};
//implementation file
const int MyClass::MinValue = -1000;
const int MyClass::MaxValue = 1000;
Ответ 2
В соответствии с "Правилом одного определения" переменная должна иметь ровно одно определение, если оно используется как odr. Это определяется стандартом С++ 11:
3.2/2 Переменная или неперегруженная функция, имя которой отображается как потенциально оцениваемое выражение, используется odr, если оно не является объектом, удовлетворяющим требованиям для отображения в постоянном выражении, а преобразование lvalue-to-rvalue немедленно применяется.
и сам ODR:
3.2/3 Каждая программа должна содержать ровно одно определение каждой не-встроенной функции или переменной, которая является odr-используемой в этой программе; не требуется диагностика.
В качестве аргументов вызова функции они не используются odr: они являются константами со значением, указанным в их объявлении, и поэтому могут отображаться в постоянном выражении; и они передаются по значению и поэтому сразу преобразуются в значения r.
Это не тот случай, когда они используются в условном выражении. Поскольку оба значения lvalues относятся к одному и тому же типу, результат условного выражения будет lvalue в соответствии с одним из довольно сложных правил, определяющих условный оператор:
5.16/4 Если второй и третий операнды являются glvalues одной и той же категории значений и имеют один и тот же тип, результат относится к типу и категории значений.
(Это правило допускает выражения типа (a?b:c)=d
.)
Таким образом, сами константы не сразу преобразуются в rvalues, и условное выражение не может появляться в постоянном выражении из-за условия выполнения; поэтому они используются как odr и поэтому нуждаются в определении.
Как вы заметили, изменение условия на константное выражение устраняет ошибку ссылки; так что изменит тип одной константы. Но выражение как таковое требует, чтобы у них было определение в одной (и только одной) единицах перевода:
const int MyClass::MinValue;
const int MyClass::MaxValue;
Ответ 3
Я собираюсь выйти на конечность и сказать, что это, вероятно, ошибка компилятора с той версией, которую вы используете. Как отметил AzzA в своем комментарии, gcc-4.5.1, похоже, прекрасно его строит, как gcc-4.3.4. Я также просто проверил это с помощью CLang 2.9, и он также принимает код.
В качестве альтернативы вы можете определить свои значения min/max с перечислениями, а не с static const. Это сохранит некоторую типизацию:
#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);
}