考虑以下代码。
class aClass
{
public:
static const int HALLO = -3;
};
int main()
{
std::vector<double > a;
std::vector<int> b;
std::vector<int> c;
int d = aClass::HALLO; //fine
a.resize(10,aClass::HALLO); //fine
b.resize(10,aClass::HALLO); // linker error c++11 and c++14
c.resize(10,(int)(double)aClass::HALLO); //fine
std::cout<<a[0]<<endl;
std::cout<<b[0]<<endl;
std::cout<<c[0]<<endl;
return 0;
}
编译与C ++ 03一起使用并产生输出:
-3
-3
-3
但是,使用C ++ 11或C ++ 14进行编译会导致链接器错误:
/tmp/cc3BARzY.o: In Funktion `main':
main.cpp:(.text+0x66): Nicht definierter Verweis auf `aClass::HALLO'
collect2: error: ld returned 1 exit status
奇怪的是,这只发生在矢量b
。如果有一个转换为double(a
)或甚至加倍并返回int(c
),代码将按预期运行。
如何解释这种行为?
前C ++ 11是std::vector::resize()
的签名
void resize( size_type count, T value = T() );
现在它反过来了
void resize( size_type count, const value_type& value );
从pass-by-value到pass-by-const-ref的变化导致callsite到ODR-use aClass::HALLO
,之前它没有。然后投掷到double
然后回到int
产生一个暂时的方式,避免使用ODR;对a.resize()
的调用也是出于同样的原因,因为int
值被隐式地转换为double
,并且参数引用与所得到的临时值绑定。
这里通常的解决方法是provide a definition为aClass::HALLO
;如果由于某种原因这对你不利,那么暂时避免使用ODR的简写就是应用一元operator+
:
b.resize(10, +aClass::HALLO);
它适用于double
矢量,但不是int
的原因很有趣。自C ++ 11以来,std::vector::resize
的签名是void resize(size_type count, const value_type& value )
。引用该对象使其成为ODR使用,因此,您的静态int
成员现在需要在应用程序的某处定义。
但是,当你使用std::vector<double>
时,你无法绑定对象的引用。相反,编译器创建一个临时的double
对象并绑定对所述临时的引用。因此,您可以避免ODR使用类的静态成员,因为创建double
临时不会ODR使用它,并且使用ODR-temporary是正常的。
如果您有类的.cpp文件,那么解决问题是微不足道的,在这种情况下,您只需在那里定义静态。但是,对于只有头的类,解决方案在C ++ 17之前并不简单,在这里你可以拥有内联变量并拥有一个非常好的解决方案:
#include <vector>
class aClass
{
public:
static const int HALLO;
};
inline const int aClass::HALLO = -3;
int main()
{
std::vector<int> b;
b.resize(10,aClass::HALLO); //fine
return 0;
}