我一直在使用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
语句中的访问与构造函数调用之间有什么区别?我测试的两个编译器中的哪一个在这里做了“正确”的事情,哪一个扩展了标准?
多玩一点,我意识到当我向g ++指定-O2标志时它也有效。
答案 0 :(得分:2)
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;
}
来自here
如果静态数据成员是const integral或const枚举类型,则可以在静态数据成员的声明中指定常量初始值设定项。此常量初始值设定项必须是整型常量表达式。请注意,常量初始值设定项不是定义。您仍然需要在封闭的命名空间中定义静态成员。
在正在运行的情况下,编译器不合规。微软是不合规的,因为微软几乎从不兼容,带有-O2的g ++有点好笑,但在任何一种情况下你都没有定义!
编辑: 根据经验,我总是在类之外定义我的静态常量成员变量,因为那时我永远不必记住它。在这种情况下,如果T不是整数类型(例如float),则类A将无效。因此,除了围绕异常做任何模式,我倾向于坚持我的模式围绕规则。