Вам нужен вспомогательный шаблон:
template<int N>
struct foo_helper
{ static constexpr int value = N * Foo{int_const<N-1>{}}.value; };
template<>
struct foo_helper<0>
{ static constexpr int value = 1; };
С этим (и только) руководством по выводу:
template<int C>
Foo(int_const<C>)
-> Foo<foo_helper<C>::value>
;
Live demo с Foo{int_const<5>{}}.value
, правильно оцененнымдо 120.
Почему это так?
Поскольку при следующем руководстве по выводу
template<int N>
Foo(int_const<N>) -> Foo<N*(Foo{int_const<N-1>{}}.value)>;
при запуске CTAD учитываются все направляющие;даже если вы предоставили более специализированное руководство (Foo<0>
), это рекурсивное руководство явно специализировано и Foo{int_const<N-1>{}}
в конечном итоге становится специализированным для N=0
, следовательно, для бесконечной рекурсии.
Введение уровня косвенности,foo_helper
нарушает эту бесконечную рекурсию: вы можете специализировать класс, а не руководство по выводам.