C ++ 11:重载无法解析递归decltype [英] C++11: Overload fails to resolve recursive decltype

查看:336
本文介绍了C ++ 11:重载无法解析递归decltype的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在下面的代码中,我试图构建一个类型的格子。例如,在 float int 之间,将结果提升为 float

In the following piece of code, I'm trying to build a lattice of types. For instance, between float and int, promote the result to 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;
};

其行为与加入

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

也可以加入

It can also be joined with a "scalar" type:

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),但让我们从更具体的开始。):

So far, so good, it works. But then, because in the real case I actually have many more such rules, I'd like to avoid duplicating the number of rules to express the commutativity of the join operation, and therefore, I express that join(scalar, wrapper) := join(wrapper, scalar) (In fact, I'd prefer something like join(v1, v2) := join(v2, v1), but let's start with something more specific.):

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的模板函数的无限扩展,我不明白。

this works properly for join(scalar, scalar), join(wrapper, scalar) and join(scalar, wrapper). But then join(wrapper, wrapper) results in an infinite expansions of template functions with both G++ 4.9 and Clang++ 3.5, which I don't understand.

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

不明白为什么重载不会减少递归。到底是怎么回事?可能有一个可能的替代实现与(类)模板专业化,但我不是寻找替代实现:我想知道这是什么问题。提前感谢。

I do not understand why overloading does not cut the recursion. What is going on? There is probably a possible alternative implementation with (class) template specialization, but I'm not looking for alternative implementations: I'd like to understand what's wrong with this one. Thanks in advance.

推荐答案

这有几个问题,其中一个导致错误。

There are several problems with this, and one of them leads to the error.

template <typename Inner1, typename T2>
auto
join(const wrapper<Inner1>& w1, const T2& value2)  // (A)
  -> wrapper<decltype(join(w1.value, value2))>;

join的名称查找通过非限定查找找到相同的函数模板,因为 trailing-return-type 是声明的一部分,并且只有已声明 。但是语法允许ADL找到相同的函数模板。 ADL的依赖名字稍后执行(从实例化的角度来说)。

The name lookup of join here will not find the same function template via unqualified lookup, as the trailing-return-type is part of the declaration, and names can only be found once they have been declared. But the syntax allows ADL to find the same function template. ADL for dependent names is performed later (from the point of instantiation).

据我理解的问题,问题来自重载解决:之前 decltype(join(w1.value,value2))尝试解析为重载,所有具有该名称的函数模板都需要实例化。

As far as I understood the problem, the issue comes from overload resolution: Before decltype(join(w1.value, value2)) tries to resolve to an overload, all function templates with that name need to be instantiated. For each function template, one instantiation is added to the overload set (if instantiation succeeds).

因此,所有加入需要实例化 。实例化包括确定返回类型。对于该特定 join 函数模板(A)的每个实例化,具有相同模板参数的相同函数模板(A)是重载分辨率集合的候选。 也就是说,为了确定哪个返回类型(A)具有,需要有重载解析,这需要确定(A)的返回类型等等。

Therefore, all joins need to be instantiated. Instantiation includes determining the return type. For every instantiation of this particular join function template (A), the same function template (A) with the same template arguments is a candidate for the overload resolution set. That is, to determine which return type (A) has, there needs to be an overload resolution, which requires to determine the return type of (A) and so on.

扣除替换永远不会在递归的任何单个步骤中失败,唯一的原因不是选择此重载是不同的函数模板 join 。并且部分排序只作为重载解析过程的一部分来检查 - 这太晚了,无法阻止进一步的实例化。

Deduction & substitution never fails in any single step of the recursion, the only reason not to choose this overload is partial ordering between the different function templates called join. And partial ordering is only checked as part of the overload resolution process -- which is too late to prevent further instantiations.

在错误消息中提及,作为实现限制发生。因此,它不属于SFINAE类别,请参阅解决表达式的SFINAE问题。因此,即使不选择这种过载,它只是存在使得程序不成形,就像

This error, as mentioned in the error message, occurs as an implementation limit. Therefore, it does not fall into the SFINAE category, see Solving the SFINAE problem for expressions. Therefore, even if this overload is not chosen, its mere existence makes the program ill-formed, just like

struct tag_for_ADL {};

template<class T>
auto foo(T p) -> decltype(foo(p));

foo(tag_for_ADL{}); // ill-formed, leads to infinite recursive instantiation

这篇关于C ++ 11:重载无法解析递归decltype的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆