Почему переменные статического члена хорошо сочетаются с тройным оператором?

Здесь сделка. У меня есть статический класс, который содержит несколько статических функций, используемых для ввода ввода. Класс содержит частную статическую переменную-член для указания, вводит ли пользователь какую-либо информацию. Каждый метод ввода проверяет, вводит ли пользователь какую-либо информацию и соответственно устанавливает переменную состояния. Я думаю, что это будет подходящее время для использования тернарного оператора. К сожалению, я не могу, потому что компилятору это не нравится.

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

Вот мой заголовочный файл:

#include <iostream>

using namespace std;

class Test {
public:
    void go ();
private:
    static const int GOOD = 0;
    static const int BAD = 1;
};

Здесь моя реализация с тернарным оператором:

#include "test.h"

void Test::go () {
    int num = 3;
    int localStatus;
    localStatus = (num > 2) ? GOOD : BAD;
}

Здесь основная функция:

#include <iostream>
#include "test.h"

using namespace std;

int main () {
    Test test = Test();
    test.go();
    return 0;
}

Когда я пытаюсь скомпилировать это, я получаю это сообщение об ошибке:

test.o: In function `Test::go()':
test.cpp:(.text+0x17): undefined reference to `Test::GOOD'
test.cpp:(.text+0x1f): undefined reference to `Test::BAD'
collect2: ld returned 1 exit status

Однако, если я заменю это:

localStatus = (num > 2) ? GOOD : BAD;

с этим:

if (num > 2) {
    localStatus = GOOD;
} else {
    localStatus = BAD;
}

Код компилируется и выполняется, как ожидалось. Какое неясное правило С++ или GCC-уголок делаются за это безумие? (Я использую GCC 4.4.1 на Ubuntu 9.10.)

Ответы

Ответ 1

Это соответствует стандарту С++. Тернарный оператор представляет собой одно значение lvalue, которое будет ссылаться на GOOD или BAD во время выполнения. Преобразование lvalue в rvalue не применяется сразу к lvalue GOOD или BAD, и для этого вам требуется определение GOOD и BAD.

См. отчет о выпуске основного языка http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#712.

В качестве обходного решения вы можете применить явные приведения к int (который читает их значения, тем самым делая преобразование lvalue в rvalue) или использует оператор, который считывает значение, например +:

localStatus = (num > 2) ? +GOOD : +BAD;

Ответ 2

class Test {
    static const int GOOD = 0;
    static const int BAD = 1;
};

Это только декларации; они не являются определениями. Вам необходимо предоставить определения статических переменных-членов вне определения класса в одном из ваших .cpp файлов:

const int Test::GOOD;
const int Test::BAD;

В качестве альтернативы для целочисленных констант часто удобнее использовать enum:

class Test {
    enum { 
        GOOD = 0,
        BAD = 1 
    };
};

Ответ 3

Ваш код выглядит хорошо для меня. И ideone соглашается: см. эту ссылку. Но это с gcc-4.3.4. Однако мой gcc-4.4.0 не принимает его. Так что какова бы ни была причина, это не очевидно.

Отредактировано для добавления: Следующий вариант компилируется под gcc-4.4.0:

int localStatus = 42 ? GOOD : BAD;

Напоминание: следующий код не компилируется:

int localStatus = (num == 42) ? GOOD : BAD;

Итак, кто-то где-то прищурился.