2017-09-14 6 views
2
#include <memory> 

template <typename T> 
class Wrapper { 
public: 
    Wrapper() = delete; 
    Wrapper(const Wrapper&) = delete; 
    Wrapper(Wrapper&&) = delete; 

    ~Wrapper() = default; 

    Wrapper(const T&) = delete; 
    Wrapper(T&& in) : instance{std::move(in)} {} 

    T instance; 
}; 

void foo(Wrapper<std::shared_ptr<int>>) {} 

int main() { 
    auto ptr = std::make_shared<int>(1); 
    foo(std::move(ptr)); 
} 

Cela a fonctionné en C++ 17 donc je ne l'ai jamais pensé mais pourquoi ce code essaye-t-il d'invoquer le constructeur de déplacement en C++ 14 ? Ne devrait-il pas être construit en place dans l'argument de la fonction? Cela ne semble pas être un problème avec C++ 17 mais ne compile pas avec C++ 14.Copier le paramètre invoque le constructeur supprimé lorsque ce constructeur ne devrait pas être appelé

La seule solution de contournement que je vois est de rendre le paramètre foo un rvalue, mais y at-il quelque chose que je peux faire pour que cela fonctionne sans faire du paramètre foo un rvalue en C++ 14?


Ma première pensée serait qu'un serait temporaire doivent être constructeur afin d'être transmis à la fonction, mais ce qui est encore plus surprenant est que même avec -fno-elide-constructors et Annulation de la suppression des déplacement constructeurs et copier les constructeurs ceux ne le font pas semble être appelé! Est-ce un bug dans gcc et clang tous les deux?

Voir https://wandbox.org/permlink/f6sa5Rm3NxZLy5P1 l'erreur Et voir le comportement étrange https://wandbox.org/permlink/Kh6CG4OVbUAjvEZz

+1

quelles sont les erreurs exactes que vous obtenez – vu1p3n0x

+0

@ vu1p3n0x les a publiées dans la question, il était dans le titre ainsi – Curious

+1

Dans votre lien pour « voir le comportement étrange », il fait constructeur de déplacement d'appel –

Répondre

4

Lorsque vous appelez foo(std::move(ptr)); vous ne donnez pas un Wrapper<std::shared_ptr<int>>. Ainsi, le compilateur génère un temporaire et l'utilise pour construire le paramètre de foo. Maintenant, cela peut être élidé et nous pouvons directement construire un Wrapper<std::shared_ptr<int>> mais le constructeur de déplacement/copie doit toujours être accessible, même s'il n'est jamais appelé.

Avec C++ 17, cela ne se produit plus. Nous avons guaranteed copy elision ce qui signifie qu'aucun temporaire n'est matérialisé et que le paramètre est directement construit.

+0

Mais alors pourquoi le constructeur de mouvement n'est jamais appelé? Même avec '-fno-elide-constructors' – Curious

+0

@Curious cosntructor requis par le langage mais l'appel peut être éliminé par l'optimisation. – Slava

+0

@Curious Parce que même si le compilateur est assez intelligent pour ne pas rendre temporaire la norme de langage dit que le constructeur approprié doit toujours être accessible. – NathanOliver