G++ (C++14) 链接器错误,用于处理 C++03 代码

G++ (C++14) linker error on working C++03 code

本文关键字:用于 处理 C++03 代码 错误 C++14 链接 G++      更新时间:2023-10-16

请考虑以下代码段。

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上。如果有强制转换到双精度(a(甚至双精度并返回int(c(,代码将按预期运行。

如何解释这种行为?

直到 C++11std::vector::resize()的签名是

void resize( size_type count, T value = T() );

现在它反而是

void resize( size_type count, T const& value );

从按值传递到按常量引用传递会导致调用站点使用 ODRaClass::HALLO而以前没有。 强制转换为double然后返回到int会产生临时,从而避免使用 ODR;对a.resize()的调用出于同样的原因,因为int值被隐式强制转换为double,并且参数引用绑定到生成的临时值。

这里通常的解决方法是为aClass::HALLO提供一个定义;如果出于某种原因你不希望这样做,那么产生临时以避免使用ODR的简写是应用一元operator+

b.resize(10, +aClass::HALLO);

它适用于double向量而不是int的原因很有趣。std::vector::resize的签名自 C++11 起void resize(size_type count, const value_type& value )。对对象的引用使其被 ODR 使用,因此,现在需要在应用程序中的某个位置定义静态int成员。

但是,当您std::vector<double>时,不能绑定对对象的引用。相反,编译器创建一个临时double对象,并将引用绑定到所述临时对象。因此,您可以避免使用 ODR 类的静态成员,因为创建临时double不会 ODR 使用它,并且使用 ODR 临时就可以了。

如果您有该类.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;
}