2017-10-13 15 views
5

Mam klasy configniezdefiniowana odniesienia do const int w shared_ptr

// config.hpp 

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

i obejmują w 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 
} 

i kompilator, że undefined reference to Config::a

i działa przy użyciu cout , ale nie działa, gdy znajduje się w konstruktorze shared_ptr.

Nie mam pojęcia, dlaczego tak się dzieje.

+0

Trzeba zdefiniować 'A' w zakresie przestrzeni nazw jak członka statycznego przed C++ 17, czyli' constexpr Config :: int a; '' Dlaczego –

+0

cout' działa? –

+1

Jest to niefortunna konsekwencja idealnego przekazywania i używania odr, które sprawia, że ​​'make_shared' nie działa. 'make_shared (int (Config :: a))' będzie także działało –

Odpowiedz

7

Zauważ, że std::make_shared trwa parametru przez referencję, co powoduje Config::a być odr-used bo to będzie zobowiązany do parametru odniesienia, wówczas wymagane jest jej definicja w zakresie przestrzeni nazw (przed C++ 17).

Z drugiej strony, std::cout << Config::a nie spowoduje Config::a być ODR wykorzystywane, ponieważ std::basic_ostream::operator<< (int) trwa parametru przez wartość, Config::a jest następnie przedmiotem lwartości do rvalue wezwanej skopiować zainicjować parametru konwersji, dlatego Config::a jest nie używa się odr.

Jeśli obiekt jest odr używany, musi istnieć jego definicja. Możesz dodać definicję (w pliku implementacji) jako

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

pamiętać, że nie może mieć inicjator.

LIVE

Ponieważ C++ 17 constexpr static data member jest niejawnie inline wtedy taka definicja nie jest wymagane ponownie, więc kod działa dobrze z C++ 17.

Jeżeli członek static danych jest zadeklarowana constexpr jest niejawnie inline i nie musi być redeclared w zakresie przestrzeni nazw. Ta redeclaracja bez inicjalizatora (wcześniej wymagana, jak pokazano powyżej) jest nadal dozwolona, ​​ale jest przestarzała.

LIVE

+0

Należy zauważyć, że definicja w obszarze przestrzeni nazw nie może znajdować się w pliku nagłówkowym; musi być w pliku .cpp, który łączysz ze wszystkim innym. –

1

Twój a jest prywatny i publiczny albo: musi poprzedzać go lub uczynić klasy A struct do uzyskania domyślnego publicznej. Ale to kompiluje pod 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 
} 
+0

edytuj jako publiczny. przepraszam za błąd. –

+1

Tak, problem prawdopodobnie dotyczy wielu jednostek tłumaczeniowych. Jeśli konstruktor dla otherClass znajduje się w innym pliku cpp, to nie będzie działać w C++ 14. –

Powiązane problemy