2014-04-29 10 views
21

Ich habe mit variadischen Vorlagen gespielt und folgendes bemerkt.Warum überschreitet diese die maximale rekursive Vorlagentiefe?

Dies funktioniert:

auto t = std::make_tuple(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16); 

Dadurch wird der Fehler (: Clang 3.4) hat eine maximale Tiefe von 256 standardmäßig gcc 4.8.2 (edit): geben

auto t2 = std::make_tuple(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17); 

jedoch Das Erstellen des Tupels direkt wird funktionieren:

std::tuple<int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int,int> t3(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17); 

Ich bemerkte dies beim Versuch, eine Vorlagenfunktion zu erstellen, die eine Vorlagenklasse zurückgibt.

template <typename...Arguments> 
struct Testing { 
    std::tuple<Arguments...> t; 
    Testing(Arguments...args) : t(args...) {} 
}; 

template <typename... Arguments> 
Testing<Arguments...> create(Arguments... args) { 
    return Testing<Arguments...>(args...); 
} 

In diesem Fall wird diese Arbeit:

auto t4 = create(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16); 

und diese werden nicht:

auto t5 = create(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17); 
+0

Wahrscheinlich, weil 'create' eine„rekursive Template“ist und wenn Sie es 17 Argumente geben braucht es eine Tiefe von 17, und wenn Sie es 16 Argumente geben, benötigt es eine Tiefe von 16, und das Limit ist (appertly) 16. –

+3

g ++ 4.8.2 benötigt eine Instantiierungstiefe von genau 231, um 'make_tuple' mit 17 Argumenten zu kompilieren. [Live-Beispiel] (http: //coliru.stacked-crooked.com/a/67d4b91e9980f2f8) Das 'make_tuple' selbst fügt nur eine Instantiierungstiefe hinzu, so weit ich sehen kann, das Problem ist wahrscheinlich' tuple' selbst. – dyp

+0

ooooooh, das erklärt meine Verwirrung –

Antwort

11

Das Problem ist nicht make_tuple, aber der Umzug Konstruktor tuple in libstdC++ (gcc4.8.2).

Bei Klassenvorlagen werden die Elementfunktionen nur bei Verwendung instanziiert. Die noexcept-Spezifikation wird ähnlich verzögert, siehe z.B. CWG issue 1330.

Wenn eine Variable von make_tuple initialisiert wird, wird der Verschiebungskonstruktor instanziiert, auch wenn er entfernt ist (z. B. um zu überprüfen, ob er fehlerhaft ist). Deshalb sehen Sie einen Unterschied zwischen nur eine tuple Variable und mit make_tuple definieren.

Der Verschiebungskonstruktor hat eine bedingte noexcept, die rekursiv implementiert wird. Daher ist für jedes Template-Argument eine konstante Anzahl zusätzlicher Instanziierungen erforderlich. Ein Auszug aus Klirren ++ 's Fehler ausgegeben, wenn die max Instanziierung Tiefe von mehr als: (halten Sie sich fest, Wand des Textes eingehenden)

 
/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/type_traits:803:24: note: in instantiation of default argument for '__test, std::_Tuple_impl &&>' required here 
     static true_type __test(int); 
         ^~~~~~~~~~~ 
/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/type_traits:803:24: note: while substituting deduced template arguments into function template '__test' [with _Tp = std::_Tuple_impl, _Arg = std::_Tuple_impl &&, $2 = ] 
     static true_type __test(int); 
        ^
/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/type_traits:117:14: note: in instantiation of template class 'std::__is_direct_constructible_impl, std::_Tuple_impl &&>' requested here 
    : public conditional::type 
      ^
/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/type_traits:818:14: note: in instantiation of template class 'std::__and_ >, std::__is_direct_constructible_impl, std::_Tuple_impl &&> >' requested here 
    : public __and_, 
      ^
/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/type_traits:896:14: note: in instantiation of template class 'std::__is_direct_constructible_new_safe, std::_Tuple_impl &&>' requested here 
    : public conditional::value, 
      ^
/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/type_traits:904:39: note: in instantiation of template class 'std::__is_direct_constructible_new, std::_Tuple_impl &&>' requested here 
    : public integral_constant, std::_Tuple_impl &&>' requested here 
    : public __is_direct_constructible 
      ^
/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/type_traits:956:39: note: in instantiation of template class 'std::__is_constructible_impl, std::_Tuple_impl &&>' requested here 
    : public integral_constant, std::_Tuple_impl &&>' requested here 
    : public conditional::type 
         ^
/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/type_traits:1042:14: note: in instantiation of template class 'std::__and_, std::_Tuple_impl &&>, std::__is_nt_constructible_impl, std::_Tuple_impl &&> >' requested here 
    : public __and_, 
      ^
/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/type_traits:1073:14: note: in instantiation of template class 'std::is_nothrow_constructible, std::_Tuple_impl &&>' requested here 
    : public is_nothrow_constructible 
      ^
/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/type_traits:1079:14: note: in instantiation of template class 'std::__is_nothrow_move_constructible_impl, false>' requested here 
    : public __is_nothrow_move_constructible_impl 
      ^
/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/type_traits:117:14: note: in instantiation of template class 'std::is_nothrow_move_constructible >' requested here 
    : public conditional::type 
      ^
/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/tuple:268:16: note: in instantiation of template class 'std::__and_, std::is_nothrow_move_constructible > >' requested here 
     noexcept(__and_, 
      ^
/usr/lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/type_traits:802:24: note: in instantiation of exception specification for '_Tuple_impl' requested here 
      = decltype(::new _Tp(declval()))> 

Wir hier die Umsetzung sehen können z.B. von is_nothrow_move_constructible in Bezug auf is_nothrow_constructible, die in Bezug auf __is_nt_constructible und so weiter implementiert ist, für 15 Instantiierungsstufen. Dies wird wie ein Aufrufstapel gedruckt, sodass Sie den Instanziierungen von unten folgen können. Diese


bedeutet, dass jedes Template-Argument für tuple erfordert 15 zusätzliche Instanziierung Ebene für diese Prüfung. Darüber hinaus sind immer 9 Stufen erforderlich (konstante Tiefe).

Daher 17 Argumente erfordern eine Instanziierung Tiefe von 17 * 15 + 9 == 264.

+1

C++ 11 empfiehlt eine maximale Instanziierungstiefe von mindestens 1024, g ++ hat eine Standardtiefe von maximal 900. Siehe den Eintrag für '-template-depth' in das Handbuch [g ++ 4.8.2] (http://gcc.gnu.org/onlinedocs/gcc-4.8.2/gcc/C_002b_002b-Dialect-Options.html#C_002b_002b-Dialect-Options). – dyp

+0

Gibt es eine Möglichkeit, dies zu umgehen? Ich meine, könnte man [this] (http://coliru.stacked-crooked.com/a/de977996dc0f2d6c) kompilieren, ohne die Instanziierungstiefe zu erhöhen? –

+0

Oder vielleicht sogar eine andere Tuple-Implementierung, die unter solchen Problemen leidet? (Ich habe boost :: tuple überprüft und es schlägt auch fehl) –

Verwandte Themen