Я играл с шаблонными классами и константами в gcc 4.8.2 и наткнулся на интересную ошибку компоновщика:
#include <iostream>
using namespace std;
template <class T, int m>
class A {
public:
static const T param = m;
T value;
A(const T &value, const T &dummy = T()) : value(value) {}
};
// Works with this
// template <class T, int m>
// const T A<T, m>::param = m;
template <class T, int m>
A<T, m> operator +(const A<T, m> &a, const A<T, m> &b) {
if (a.param != b.param) exit(1);
// Works if I replace a.param with 0
return A<T, m>(a.value + b.value, a.param);
}
int main() {
A<int, 2> v = A<int, 2>(1) + A<int, 2>(2);
cout << v.value << endl;
return 0;
}
Компиляция кода в текущем состоянии дает ошибку компоновщика, сообщая мне, что A::param
не определено.
Используя этот код в Visual Studio 2008, он без проблем компилирует и связывает. На gcc он компилируется, когда я использую внешнее объявление param
постоянный, или если я заменю a.param
с 0 или ничего на указанной строке.
Я не понимаю, почему строка, содержащая if
заявление может использовать a.param
без ошибки компиляции, пока я не могу пройти a.param
конструктору, не объявляя его внешне.
Итак, мой вопрос: когда мне нужно объявить param
внешне, а какая разница между доступом в if
оператор и вызов конструктора? Какой из двух протестированных мной компиляторов делает «правильную» вещь здесь, а какой расширил стандарт?
Поиграв немного больше, я понял, что это также работает, когда я указываю флаг -O2 для g ++.
template <class T, int m>
class A {
public:
static const T param = m;
T value;
A(const T &value, const T &dummy = T()) : value(value) {}
};
// Works with this
// template <class T, int m>
// const T A<T, m>::param = m;
//gotta define it here!
template <class T, int m>
const T A<T, m>::param;
template <class T, int m>
A<T, m> operator +(const A<T, m> &a, const A<T, m> &b) {
if (a.param != b.param) exit(1);
// Works if I replace a.param with 0
return A<T, m>(a.value + b.value, a.param);
}
int main() {
A<int, 2> v = A<int, 2>(1) + A<int, 2>(2);
std::cout << v.value << std::endl;
return 0;
}
от Вот
Если член статических данных имеет константный интеграл или константный тип перечисления, вы можете указать постоянный инициализатор в объявлении статического члена данных. Этот инициализатор констант должен быть выражением интегральной константы. Обратите внимание, что инициализатор констант не является определением. Вам все еще нужно определить статический член во вложенном пространстве имен.
В тех случаях, когда это работает, компиляторы несовместимы. Microsoft несовместима, потому что Microsoft почти никогда не совместима, g ++ с -O2 довольно забавно, но в любом случае у вас нет определения!
Редактировать:
Как правило, я всегда определяю свои статические постоянные переменные-члены вне класса, потому что тогда мне никогда не придется их запоминать. В этом случае класс A был бы недействительным, если бы T был чем-то отличным от целочисленного типа, например, с плавающей точкой. Поэтому вместо того, чтобы создавать какие-либо шаблоны вокруг исключения, я склонен придерживаться своих шаблонов вокруг правила.
Других решений пока нет …