2017-10-13 7 views
5

J'ai une classe Configréférence définie à l'intérieur const int shared_ptr

// config.hpp 

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

, parmi lesquels figurent 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 
} 

et compilateur dit que undefined reference to Config::a

et cela fonctionne lors de l'utilisation cout , mais ne fonctionne pas lorsque dans shared_ptr constructeur.

Je ne sais pas pourquoi cela se produit.

+0

Vous devez définir 'au champ d'a' espace de noms comme un membre statique avant C++ 17, par exemple' constexpr int Config :: a; '' Pourquoi –

+0

cout' fonctionne? –

+1

C'est une conséquence malheureuse de la transmission parfaite et de l'utilisation de l'odr qui fait que 'make_shared' ne fonctionne pas. 'make_shared (int (Config :: a))' fonctionnera également –

Répondre

7

Notez que std::make_shared prend paramètre par référence, ce qui provoque Config::a être odr-used car il sera lié au paramètre de référence, sa définition à la portée de l'espace de noms est nécessaire (avant C++ 17).

D'autre part, std::cout << Config::a ne causera pas Config::a à ODR utilisés, car std::basic_ostream::operator<< (int) prend paramètre par valeur, Config::a est ensuite soumis à lValue à rvalue conversion demandé de copier-initialiser le paramètre donc Config::a est pas odr-utilisé.

Si un objet est utilisé, sa définition doit exister. Vous pouvez ajouter la définition (dans un fichier d'implémentation) en tant que

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

Notez qu'il ne peut pas avoir d'initialiseur.

LIVE

Depuis C++ 17 constexpr static data member est en ligne alors implicitement une telle définition n'est pas nécessaire de nouveau, de sorte que votre code fonctionne bien de 17 C++.

Si un membre de données static est déclarée constexpr, il est implicitement inline et n'a pas besoin d'être redéclarée à périmètre d'espace de noms. Cette redéclaration sans initialiseur (précédemment requise comme indiqué ci-dessus) est toujours autorisée, mais elle est obsolète.

LIVE

+0

Notez que la définition à la portée de l'espace de noms ne peut pas être dans le fichier d'en-tête; il doit être dans un fichier .cpp que vous liez avec tout le reste. –

1

Votre un est privé, et soit un public: doit précéder ou faire la classe struct pour obtenir du public par défaut. Mais cette compile sous 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

éditer a comme public. Désolé pour l'erreur. –

+1

Oui, le problème concerne probablement plusieurs unités de traduction. Si le constructeur d'otherClass est dans un fichier cpp différent, cela ne fonctionnera pas dans C++ 14. –