undefined reference to const int within shared_ptr
Asked Answered
S

2

6

I have a Config class

// config.hpp

class Config {
  public:
    static constexpr int a = 1;
    static constexpr int b = 1;
}

and include in main.cpp

// main.cpp
#include "config.hpp"
int main () {
  std::cout << Config::a << std::endl; // this is ok
  std::shared_ptr<otherClass> stream = std::make_shared<otherClass>( 
Config::a); // compile error
}

and compiler said that undefined reference to Config::a

and it works when using cout, but not work when within shared_ptr constructor.

I have no idea why this happens.

Sharla answered 13/10, 2017 at 3:24 Comment(4)
You need to define a at namespace scope like a static member prior to c++17, ie constexpr int Config::a;Sheepherder
Why cout works?Sharla
It's an unfortunate consequence of perfect forwarding and odr-usage that makes the make_shared not work. make_shared<otherClass>(int(Config::a)) will also workSheepherder
ok. hope you explain that in detail. thanks!Sharla
S
8

Note that std::make_shared takes parameter by reference, which causes Config::a to be odr-used because it'll be bound to the reference parameter, then its definition at namespace scope is required (before C++17).

On the other hand, std::cout << Config::a won't cause Config::a to be odr-used, because std::basic_ostream::operator<< (int) takes parameter by value, Config::a is then subject to lvalue-to-rvalue conversion requested to copy-initialize the parameter, therefore Config::a is not odr-used.

If an object is odr-used, its definition must exist. You can add the definition (in an implementation file) as

constexpr int Config::a; // only necessary before C++17

Note that it cannot have an initializer.

LIVE

Since C++17 constexpr static data member is implicitly inline then such definition is not required again, so your code works well from C++17.

If a static data member is declared constexpr, it is implicitly inline and does not need to be redeclared at namespace scope. This redeclaration without an initializer (formerly required as shown above) is still permitted, but is deprecated.

LIVE

Supralapsarian answered 13/10, 2017 at 3:37 Comment(1)
Note that the definition at namespace scope can't be in the header file; it must be in a .cpp file you link with everything else.Ouphe
C
1

Your a is private, and either a public: needs to preceed it or make the class a struct to get default public. But this compiles under C++14 https://godbolt.org/g/tS4M1Z

#include <iostream>
#include <memory>

struct Config {
  static constexpr int a = 1;
  static constexpr int b = 1;
};

struct otherClass {
    otherClass( int c ) { }
};

int main () {
  std::cout << Config::a << std::endl; // this is ok
  std::shared_ptr<otherClass> stream = std::make_shared<otherClass>( Config::a ); // compile error
}
Cecum answered 13/10, 2017 at 3:37 Comment(1)
Yes, the issue probably actually has to do with multiple translation units. If the constructor for otherClass is in a different cpp file, then this won't work in C++14.Ouphe

© 2022 - 2024 — McMap. All rights reserved.