2016-05-12 15 views
6

Meine grundlegende Idee war meine eigene Klasse von std :: tuple abzuleiten einige Helfer Typen innerhalb wie folgt zu erhalten:Verwirrung, während die sich aus std :: Tupel kann nicht std :: Griff

template <typename ... T> 
class TypeContainer: public std::tuple<T...> 
{ 
    public: 
     using BaseType = std::tuple<T...>; 
     static const size_t Size = sizeof...(T); 
     TypeContainer(T... args):std::tuple<T...>(args...){}; 

     using index_sequence = std::index_sequence_for<T...>; 
}; 

Jetzt ich versuche, den Code wie folgt zu verwenden:

using MyType_tuple_with_empty =   std::tuple<  std::tuple<float,int>, std::tuple<>, std::tuple<int>>; 
using MyType_typecontainer_with_empty = TypeContainer< TypeContainer<float,int>, TypeContainer<>, TypeContainer<int>>; 

using MyType_tuple_non_empty =   std::tuple<  std::tuple<float,int>, std::tuple<int>, std::tuple<int>>; 
using MyType_typecontainer_non_empty = TypeContainer< TypeContainer<float,int>, TypeContainer<int>, TypeContainer<int>>; 

template <typename T> 
void Do(const T& parms) 
{ 
    // The following lines result in errors if TypeContainer with 
    // empty element comes in. The empty element is in std::get<1> which 
    // is NOT accessed here! 
    std::cout << std::get<0>(std::get<0>(parms)) << " "; 
    std::cout << std::get<1>(std::get<0>(parms)) << std::endl; 

    std::cout << std::get<0>(std::get<2>(parms)) << std::endl; 
} 


int main() 
{ 
    MyType_tuple_with_empty   p1{{ 1.2,3},{},{1}}; 
    Do(p1); 

    MyType_typecontainer_with_empty p2{{ 1.2,3},{},{1}}; 
    Do(p2); // << this line raise the error 

    MyType_tuple_non_empty   p3{{ 1.2,3},{4},{1}}; 
    Do(p3); 

    MyType_typecontainer_non_empty p4{{ 1.2,3},{4},{1}}; 
    Do(p4); 
} 

Wenn ich mit Do(p2) kompilieren bekomme ich folgende Fehlermeldung:

error: no matching function for call to ' get(const TypeContainer<TypeContainer<float, int>, TypeContainer<>, TypeContainer<int> >&) '

Kann jemand erklären, warum die Existenz eines leeren in Verbindung mit std::get zu diesem Problem führt?

Edit: Zusätzliche Informationen:

Die Linien

MyType_tuple_with_empty   p1{{{ 1.2,3},{},{1}}}; 
MyType_tuple_non_empty   p3{{ 1.2,3},{4},{1}}; 

kann nicht mit gcc5.2.0 kompiliert, aber mit gcc6.1.0. Das ist ein bisschen mysteriös, weil ich mich erinnere, dass der Konstruktor des Tupels tatsächlich explizit ist. Warum funktioniert das mit gcc6.1.0? Aber das ist nicht das Problem, das ich für :-) Suche

Noch ein Hinweis: Der Code, ich habe Probleme mit scheint mit clang3.5.0 zu kompilieren.

Ein bisschen schwer zu verstehen ...

Edit2: die Fehlerlisten Graben durch (lang :-)) Ich fand:

/opt/linux-gnu_5.2.0/include/c++/5.2.0/tuple|832 col 5| note: template argument deduction/substitution failed: main.cpp|104 col 45| note: ' std::tuple<_Elements ...> ' is an ambiguous base class of ' TypeContainer<TypeContainer<float, int>, TypeContainer<>, TypeContainer<int> > ' || std::cout << std::get<0>(std::get<0>(parms)) << " " ;

Es scheint, dass in libg ++ jemand mehrmals ableitet von irgendeinem Tupeltyp, der eine zerbrochene Bibliothek zu sein scheint. Die Suche nach diesem Thema bringt mich zu: Empty nested tuples error

Ist das wirklich zusammen? Gleicher Fehler oder ein neues Geschäft :-)

+0

vielleicht ein [vereinfachter Fall] (http://coliru.stacked-crooked.com/a/8de00d9fcaa3df1c) –

Antwort

2

Leider müssen Sie Ihre Container-Versionen Funktionen erhalten hinzuzufügen:

template <std::size_t I, typename ...T> 
decltype(auto) get(TypeContainer<T...>&& v) 
{ 
    return std::get<I>(static_cast<std::tuple<T...>&&>(v)); 
} 
template <std::size_t I, typename ...T> 
decltype(auto) get(TypeContainer<T...>& v) 
{ 
    return std::get<I>(static_cast<std::tuple<T...>&>(v)); 
} 
template <std::size_t I, typename ...T> 
decltype(auto) get(TypeContainer<T...> const& v) 
{ 
    return std::get<I>(static_cast<std::tuple<T...> const&>(v)); 
} 

Und get nicht std::get in Do Art von Funktionen verwenden. Compiler kann Namespace aus Argumenten auswählen.

Ich denke, ich bin mir nicht sicher, dass dies ist, weil gcc EBO - Empty Base Optimization - in seinen Tupeln implementiert hat. Was genau der Grund ist, ist ziemlich schwer zu erraten. Sie könnten dies in gcc bugzilla melden.


BTW, es ist nicht gute Gewohnheit, von STD-Klassen abzuleiten. Wenn Sie mit der Erstellung beginnen, nicht mit der Vererbung, dann müssten Sie Ihre eigenen get Funktionen bereitstellen, und Sie würden diesen Fehler nicht beobachten und wahrscheinlich viel Zeit sparen.

+0

Jetzt lese ich Ihre Frage sorgfältig ... – PiotrNycz

+0

Vielen Dank für Ihre Antwort. Es gibt zwei Dinge, über die ich nachdenke: Erstens: Der Zugriff auf ein Tupel mit einem leeren Tupel sollte sich nicht anders verhalten, als auf ein Tupel mit nicht leeren Tupeln zuzugreifen. Zweitens: Klang funktioniert.Ich werde Ihren Code als Workarround für das Problem versuchen, aber ich glaube, es gibt einen Bug in libg ++ ... einen anderen. Ich habe bereits eine Liste der gemeldeten Fehler ... ohne Antwort :-( – Klaus

+0

Ich bin fast 99% sicher, dass dies daran liegt, dass EBO - ich habe in die Implementierung geschaut - es gibt einige magische Dinge, die gemacht werden, um EBO zu haben - das ist nicht einfach lese, ich habe keine Zeit, dies zu tun.Bitte berichte das. Und erwäge, Komposition zu verwenden.CLang hat wahrscheinlich EBO nicht, oder implementiert es in besserer Art. – PiotrNycz