简体   繁体   English

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

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

Consider the following piece of code. 考虑下面的代码。

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;
}

Compiling works with C++03 and yields the output: 编译使用C ++ 03并产生输出:

-3
-3
-3

Compiling with C++11 or C++14, however, leads to a linker error: 但是,使用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

Weirdly enough, this only happens for vector b . 奇怪的是,这仅发生在向量b If there is a cast to double( a ) or even to double and back to int ( c ), the code runs as expected. 如果强制转换为double( a )甚至是double并返回int( c ),则代码将按预期运行。

How can this behaviour be explained? 如何解释这种行为?

Pre-C++11 the signature of std::vector::resize() was 在C ++ 11之前, std::vector::resize()的签名为

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

Now it is instead 现在是

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

The change from pass-by-value to pass-by-const-ref causes the callsite to ODR-use aClass::HALLO where previously it did not. 从值传递到常量引用的更改导致呼叫站点使用 aClass::HALLO HALLO进行ODR ,而以前则没有。 Casting to double then back to int yields a temporary in a way that avoids ODR-use; 转换成double然后返回int可以避免ODR的使用,从而产生一个临时值。 the call to a.resize() works for the same reason, as the int value is implicitly cast to a double and the argument reference is bound to the resulting temporary. 调用a.resize()的原因相同,因为将int值隐式转换为double ,并将参数引用绑定到结果临时字段。

The usual fix here is to provide a definition for aClass::HALLO ; 通常的解决方法是为aClass::HALLO 提供定义 if for some reason that's undesirable for you, a shorthand for yielding a temporary to avoid ODR-use is to apply unary operator+ : 如果由于某种原因您不希望使用此方法,则可以使用一元运算operator+来产生临时避免ODR的捷径:

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

The reason why it works for double vector, but not int is funny. 它适用于double向量而不是int原因很有趣。 The signature for std::vector::resize is void resize(size_type count, const value_type& value ) since C++11. 从C ++ 11开始, std::vector::resize的签名为void resize(size_type count, const value_type& value ) Taking a reference to the object makes it ODR-used, and because of that, your static int member now needs to be defined somewhere in your application. 对对象的引用使其成为ODR使用,因此,现在需要在应用程序中的某个位置定义静态int成员。

However, when you std::vector<double> , you can't bind a reference to the object. 但是,当您使用std::vector<double> ,无法将引用绑定到该对象。 Instead, compiler creates a temporary double object and bind the reference to said temporary. 而是,编译器创建一个临时的double对象,并将引用绑定到该临时对象。 Because of that, you avoid ODR-using the static member of the class, as creating a double temporary doesn't ODR-use it, and ODR-using temporary is fine. 因此,您避免使用类的静态成员进行ODR,因为创建一个double临时不使用它的ODR,并且使用ODR的临时也是可以的。

Fixing the issue is trivial if you have .cpp file for the class, in which case you simply define your static there. 如果您有该类的.cpp文件,则解决此问题很简单,在这种情况下,您只需在其中定义静态即可。 However, for header-only class the solution is not trivial until C++17, where you can have inline variables and have a very nice solution: 但是,对于仅标头类,该解决方案在C ++ 17之前并不简单,在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;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM