В следующем фрагменте кода я пытаюсь построить решетку типов. Например, между float
и int
, продвиньте результат до float
:
float join(float f, int) { return f; }
float join(float f, float) { return f; }
Затем введем тип wrapper
:
template <typename Inner>
struct wrapper
{
using inner_t = Inner;
inner_t value;
};
поведение которого с операцией join
вполне естественно:
template <typename Inner1, typename Inner2>
auto
join(const wrapper<Inner1>& w1, const wrapper<Inner2>& w2)
-> wrapper<decltype(join(w1.value, w2.value))>
{
return {join(w1.value, w2.value)};
}
Он также может быть join
ed с "скалярным" типом:
template <typename Inner1, typename T2>
auto
join(const wrapper<Inner1>& w1, const T2& value2)
-> wrapper<decltype(join(w1.value, value2))>
{
return {join(w1.value, value2)};
}
Пока, так хорошо, это работает. Но тогда, поскольку в реальном случае у меня на самом деле есть еще много таких правил, я бы хотел избежать дублирования числа правил, чтобы выразить коммутативность операции join
, и поэтому я выражаю, что join(scalar, wrapper) := join(wrapper, scalar)
(на самом деле, Я бы предпочел что-то вроде join(v1, v2) := join(v2, v1)
, но начнем с чего-то более конкретного.):
template <typename T1, typename Inner2>
auto
join(const T1& value1, const wrapper<Inner2>& w2)
-> decltype(join(w2, value1))
{
return join(w2, value1);
}
это работает правильно для join(scalar, scalar)
, join(wrapper, scalar)
и join(scalar, wrapper)
. Но тогда join(wrapper, wrapper)
приводит к бесконечным разложениям шаблонных функций как с g++ 4.9, так и с Clang++ 3.5, которые я не понимаю.
int main()
{
int i;
float f;
wrapper<float> fr;
join(f, i);
join(fr, i);
join(i, fr);
join(fr, fr); // Loops.
}
Clang:
clang++-mp-3.5 -std=c++11 bar.cc
bar.cc:21:5: fatal error: recursive template instantiation exceeded maximum depth of
256
join(const wrapper<Inner1>& w1, const T2& value2)
^
bar.cc:29:5: note: while substituting deduced template arguments into function
template 'join' [with T1 = wrapper<float>, Inner2 = float]
join(const T1& value1, const wrapper<Inner2>& w2)
^
GCC:
g++-mp-4.9 -std=c++11 bar.cc
bar.cc:30:34: error: template instantiation depth exceeds maximum of 900 (use -ftemplate-depth= to increase the maximum) substituting 'template<class T1, class Inner2> decltype (join(w2, value1)) join(const T1&, const wrapper<Inner2>&) [with T1 = <missing>; Inner2 = <missing>]'
-> decltype(join(w2, value1))
^
bar.cc:30:34: recursively required by substitution of 'template<class T1, class Inner2> decltype (join(w2, value1)) join(const T1&, const wrapper<Inner2>&) [with T1 = wrapper<float>; Inner2 = float]'
bar.cc:30:34: required by substitution of 'template<class T1, class Inner2> decltype (join(w2, value1)) join(const T1&, const wrapper<Inner2>&) [with T1 = wrapper<float>; Inner2 = float]'
bar.cc:43:18: required from here
Я не понимаю, почему перегрузка не режет рекурсию. Что происходит? Вероятно, есть возможная альтернативная реализация с помощью специализированной (класс), но я не ищу альтернативных реализаций: я хотел бы понять, что с этим не так. Спасибо заранее.