2016-04-13 18 views
4

Sehr geehrte Mitprogrammierer,Std :: Tuple mit generischen Typen wie boost :: any

der folgende Code gibt mir einige Kopfschmerzen. Es versucht ein 'generisches' Objekt (= Objekt, das aus irgendwas konstruiert werden kann) zu einem Tupel hinzuzufügen und dann dieses Tupel zu kopieren.

#include <tuple> 
    #include <iostream> 
    #include <typeinfo> 


    struct anything 
    { 
     anything() 
     {} 

     anything(const anything&) 
     { 
      std::cout << "Called copy c'tor" << std::endl; 
     } 

     template<class T> 
     anything(T arg) 
     { 
      std::cout << "Called c'tor with with argument of type " << typeid(arg).name() << std::endl; 
      // new T(arg); // causes stack overflow 
     } 
    }; 


    int main() 
    { 
     std::tuple<anything> t; 
     // 
     std::cout << "Copy constructing t2, expecting copy c'tor to be called." << std::endl; 
     std::tuple<anything> t2(t); 

     return 0; 
    } 

Mit VS 2015 Update 2 es nicht einmal kompilieren, die Linie

std::tuple<anything> t2(t); 

löst einen Compiler-Fehler tief in tuple.h. Mit gcc 5.3.1 kompiliert es, aber die Ausgabe ist nicht das, was ich erwarten würde:

Kopieren Sie das Konstruieren t2, erwarte Kopie c'tor aufgerufen werden.
Genannt Kopie c'tor
Genannt c'tor mit mit dem Argument vom Typ St5tupleIJ8anythingEE

Was ich nicht verstehe, ist die letzte Zeile, das heißt, warum der Templat-Konstruktor mit std :: tuple als Argument aufgerufen wird?

Dies ist eigentlich ein Problem der realen Welt. In meiner Anwendung verwende ich ein boost :: Signal der Unterzeichnung

typedef boost::type_erasure::any 
     <boost::mpl::vector< 
     boost::type_erasure::typeid_<>, 
     boost::type_erasure::copy_constructible<>, 
     boost::type_erasure::less_than_comparable<>, 
     boost::type_erasure::equality_comparable<>, 
     boost::type_erasure::relaxed 
     >> KeyType; 

boost::signals2::signal<void(const KeyType&)> 

Boost-Signale intern das Argument in einem Tupel wickeln, bevor die Schlitz Funktion mit ihm rief, die schließlich in einem Stapelüberlauf führt, weil das Tupel c'tor ruft den any c'tor mit tuple als Argument an und der any c'tor ruft dann das tuple c'tor mit 'any of tuple' auf und so weiter und weiter und weiter ...

+0

Machen Sie den Template-Konstruktor 'explizit' –

+0

Vielen Dank für die tollen Antworten. Ich habe Barry's akzeptiert, weil es ein bisschen vollständiger ist. –

+0

Dieses Verhalten von Std :: Tupel scheint nicht intuitiv, um es gelinde auszudrücken. Sollte nicht std :: tuple eine Kopierkonstruktorüberladung haben, die einen Verweis auf nicht-const nimmt: tuple (tuple & other) , um sicherzustellen, dass die Zeile std :: tuple t2 (t); macht eine Kopie? –

Antwort

2

Lassen Sie uns die Überladungsauflösung durch für:

std::tuple<anything> t2(t); 

Wir haben drei tragfähige constructors zur Verfügung:

explicit tuple(const Types&... args); // (2) with Types = [anything] 

template< class... UTypes > 
explicit tuple(UTypes&&... args);  // (3) with UTypes = [std::tuple<anything>&] 

tuple(const tuple& other) = default; // (8) 

, die diese Argumentlisten haben:

tuple(const anything&);    // (2) 
tuple(std::tuple<anything>&);  // (3) 
tuple(std::tuple<anything> const&); // (8) 

(2) beinhaltet ein benutzerdefinierte Umwandlung während (3) und (8) sind exakte Übereinstimmungen. Wenn es darum geht Bindungen zu verweisen:

Standard-Umwandlungsfolge S1 ist eine bessere Umwandlungsfolge als Standard-Umwandlungsfolge S2, wenn [...] S1 und S2 sind Referenzbindungen (8.5.3), und die Typen zu die Referenzen beziehen sich auf den gleichen Typ mit Ausnahme von CV-Qualifikationsmerkmalen der obersten Ebene, und der Typ, auf den die durch S2 initialisierte Referenz verweist ist mehr cv-qualifiziert als der Typ, auf den sich die durch S1 initialisierte Referenz bezieht.

So (3) ist bevorzugt - da es weniger cv-qualifiziert als (8). Dieser Konstruktor ruft anything(std::tuple<anything>) auf.


Soweit Lösungen, was Sie brauchen, ist für (3) in diesem Fall nicht in Betracht gezogen werden - müssen wir es nicht ein gangbarer Weg zu machen (da (8) bereits (2) bevorzugt wird). Derzeit, ist die einfachste Sache einfach Ihren Konstruktor explicit zu machen:

template<class T> 
explicit anything(T arg) { ... } 

das funktioniert seit (3) angegeben in Bezug auf is_convertible<>, die auf explicit Konvertierungen falsch zurück. Dies wird jedoch derzeit als ein Defekt angesehen und wird wahrscheinlich in der Zukunft geändert werden - denn schließlich sind wir , die hier jeden Aspekt explizit konstruieren, so dass die explicit Konstruktoren immer noch berücksichtigt werden sollten!

Sobald das passiert, sind Sie ein bisschen Pech, soweit die direkte Kopie Konstruktion geht. Sie müssten etwas tun, wie Ihren anything Konstruktor für tuple deaktivieren? Das scheint ... nicht großartig. Aber in diesem Fall, Kennzeichnung, dass Konstruktor explicit noch für copy-Initialisierung funktionieren würde:

std::tuple<anything> t2 = t; 

die nun auch funktioniert ohne Kennzeichnung den anything Konstruktor explicit aufgrund des gleichen oben genannten Fehler.

1

Wenn man sich die Implementierung von das Tupel werden Sie feststellen, dass

_Tuple_val<_This> _Myfirst; // the stored element 
... 
template<class _This2, 
    class... _Rest2, 
    class = typename _Tuple_enable<tuple<_This2, _Rest2...>, _Myt>::type> 
    explicit tuple(_This2&& _This_arg, _Rest2&&... _Rest_arg) 
    : _Mybase(_STD forward<_Rest2>(_Rest_arg)...), 
     _Myfirst(_STD forward<_This2>(_This_arg)) 
    { // construct from one or more moved elements 
    } 

Der Ersteller des Tup le übergibt das erste Argument an den Konstruktor des ersten Tupel-Elements. Als Variable t hat Typ std::tuple<anything> Compiler findet die beste Übereinstimmung für den Aufbau anything mit t - eine Vorlage Konstruktor.

ein Tupel kopieren müssen Sie einfach

std::tuple<anything> t2 = t; 

UPD schreiben.

Laut Norm std :: tuple bietet folgende Konstruktoren:

template <class... UTypes> 
explicit constexpr tuple(const Types&...); 

template <class... UTypes> 
constexpr tuple(const tuple<UTypes...>&); 

Und anscheinend ist die Vorlage Konstruktor eine bessere Übereinstimmung als Copykonstruktor des Tupels.

Verwandte Themen