为什么自动返回类型会改变重载分辨率?

时间:2014-11-03 08:34:33

标签: c++ auto c++14 return-type-deduction

感谢decltype作为返回类型,C ++ 11使得引入装饰器非常容易。例如,考虑这个类:

struct base
{
  void fun(unsigned) {}
};

我想用其他功能来装饰它,因为我将使用不同类型的装饰多次这样做,所以我首先介绍一个decorator类,它只是将所有内容转发到base。在真实的代码中,这是通过std::shared_ptr完成的,这样我就可以删除装饰并恢复“裸体”#34;对象,一切都是模板化的。

#include <utility> // std::forward
struct decorator
{
  base b;

  template <typename... Args>
  auto
  fun(Args&&... args)
    -> decltype(b.fun(std::forward<Args>(args)...))
  {
    return b.fun(std::forward<Args>(args)...);
  }
};

完美转发和decltype非常棒。在实际代码中,我实际上使用的是一个只需要函数名称的宏,其余的都是样板文件。

然后,我可以引入一个derived类,为我的对象添加功能(derived是不合适的,同意的,但它有助于理解derived是一种{ {1}},虽然不是通过继承)。

base

然后C ++ 14带来了返回类型推导,它允许以更简单的方式编写内容:删除转发函数的struct foo_t {}; struct derived : decorator { using decorator::fun; // I want "native" fun, and decorated fun. void fun(const foo_t&) {} }; int main() { derived d; d.fun(foo_t{}); } 部分:

decltype

然后然后就会中断。是的,至少根据GCC和Clang的说法,这个:

struct decorator
{
  base b;

  template <typename... Args>
  auto
  fun(Args&&... args)
  {
    return b.fun(std::forward<Args>(args)...);
  }
};

与此不相同(问题不是 template <typename... Args> auto fun(Args&&... args) -> decltype(b.fun(std::forward<Args>(args)...)) { return b.fun(std::forward<Args>(args)...); } }; auto):

decltype(auto)

重载决议似乎完全不同,它的结尾如下:

  template <typename... Args>
  auto
  fun(Args&&... args)
  {
    return b.fun(std::forward<Args>(args)...);
  }
};

我理解失败:我的电话(clang++-mp-3.5 -std=c++1y main.cc main.cc:19:18: error: no viable conversion from 'foo_t' to 'unsigned int' return b.fun(std::forward<Args>(args)...); ^~~~~~~~~~~~~~~~~~~~~~~~ main.cc:32:5: note: in instantiation of function template specialization 'decorator::fun<foo_t>' requested here d.fun(foo_t{}); ^ main.cc:7:20: note: passing argument to parameter here void fun(unsigned) {} ^ )与d.fun(foo_t{})的签名不完全匹配,后者需要derived::fun,所以非常渴望const foo_t&踢in(我们知道decorator::fun如何非常不耐烦地绑定到任何完全不匹配的东西)。因此,它会将此转发给无法处理Args&&...的{​​{1}}。

如果我更改base::fun以取foo_t代替derived::fun,那么它会按预期工作,这表明问题确实存在{{1}之间的竞争}和foo_t

然而,为什么这个表现出回归式扣除?而更确切地说为什么是委员会选择的这种行为?

为了让事情更容易,在Coliru:

谢谢!

1 个答案:

答案 0 :(得分:18)

看看这个电话:

d.fun(foo_t{});

您创建一个临时(即右值),将其作为参数传递给函数。那你觉得怎么样?

  • 首先尝试绑定到参数Arg&&,因为它可以接受rvalue ,因为返回类型扣除无效(由于foo_t无法转换为unsigned int,因为b.fun(std::forward<Args>(args)...)被证明是无效的表达式),如果您使用decltype(expr),此功能将被拒绝作为返回类型,在这种情况下SFINAE进入图片。但是,如果你只使用auto,那么SFINAE就不会出现,并且错误被归类为硬错误,导致编译失败。

  • 如果SFINAE在第一种情况下有效,则会调用接受foo_t const&作为参数的第二个重载。