关于工作C ++ 03代码的G ++(C ++ 14)链接器错误

问题描述 投票:0回答:2

考虑以下代码。

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++ static linker g++
2个回答
7
投票

前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 definitionaClass::HALLO;如果由于某种原因这对你不利,那么暂时避免使用ODR的简写就是应用一元operator+

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

3
投票

它适用于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;
}
© www.soinside.com 2019 - 2024. All rights reserved.