Подтвердить что ты не робот

Почему это превышает максимальную глубину рекурсивного шаблона?

Я играл с вариативными шаблонами и заметил следующее.

Это отлично работает:

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

Это даст ошибку ( gcc 4.8.2 (править: Clang 3.4) имеет максимальную глубину 256 по умолчанию):

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

Однако создание набора напрямую будет работать:

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);

Я заметил это, пытаясь создать шаблонную функцию, которая возвращает шаблонный класс.

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...);
}

В этом случае это будет работать:

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

и это не будет:

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

Ответ 1

Проблема не make_tuple, а конструктор перемещения tuple в libstdС++ (gcc4.8.2).

Для шаблонов классов функции-члены генерируются только при использовании. Спецификация noexcept также откладывается аналогично, см., Например, CWG-выпуск 1330.

При инициализации переменной из make_tuple создается конструктор перемещения, даже если он отменен (например, чтобы проверить, не плохо ли он сформирован). Вот почему вы видите разницу между простое определение переменной tuple и использование make_tuple.

Конструктор перемещения имеет условный noexcept, который реализуется рекурсивно. Поэтому для каждого аргумента шаблона требуется постоянное количество дополнительных экземпляров. Выдержка из вывода ошибки clang++ при превышении максимальной глубины инсталляции: (скопируйте себя, набирая текст)

/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()))>

Мы можем видеть здесь реализацию, например. is_nothrow_move_constructible в терминах is_nothrow_constructible, который реализован в терминах __is_nt_constructible и т.д., для 15 уровней инстанцирования. Это печатается как стек вызовов, поэтому вы можете следить за экземплярами, начиная со дна.


Это означает, что для каждого аргумента шаблона для tuple требуется 15 дополнительных уровней инстанцирования. Кроме того, всегда требуются 9 уровней (постоянная глубина).

Следовательно, 17 аргументов требуют глубины реализации 17 * 15 + 9 == 264.