Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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:

-3
-3
-3

Compiling with C++11 or C++14, however, leads to a linker error:

/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. If there is a cast to double(a) or even to double and back to int (c), the code runs as expected.

How can this behaviour be explained?

like image 375
Ricardo Avatar asked Feb 24 '26 01:02

Ricardo


1 Answers

Until C++11 the signature of std::vector::resize() was

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

Now it is instead

void resize( size_type count, T const& 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. Casting to double then back to int yields a temporary in a way that avoids ODR-use; 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.

The usual fix here is to provide a definition for 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+:

b.resize(10, +aClass::HALLO);
like image 63
ildjarn Avatar answered Feb 25 '26 14:02

ildjarn