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

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

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

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

我想用额外的功能来装饰它,因为我会用不同种类的装饰做几次,我首先介绍一个 decorator 类,它简单地将所有内容转发到 base。在实际代码中,这是通过 std::shared_ptr 完成的,这样我就可以移除装饰并恢复“裸”对象,并且一切都是模板化的。

#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 是一种 base,虽然不是通过继承)。

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

然后C++14来了,带有返回类型推导,允许用更简单的方式写东西:去掉转发函数的decltype部分:

struct decorator
{
  base b;

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

然后然后它坏了。是的,至少根据 GCC 和 Clang,这是:

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

不等同于此(问题不在于 autodecltype(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&,所以非常急切的 decorator::fun 开始发挥作用(我们知道 Args&&... 是如何非常不耐烦地绑定(bind)到任何不完全匹配)。所以它把它转发给不能处理 foo_tbase::fun

如果我将 derived::fun 更改为采用 foo_t 而不是 const foo_t&,那么它会按预期工作,这表明确实这里的问题是 derived::fundecorator::fun 之间存在竞争。

但是为什么这会显示返回类型的扣除???更准确地说,为什么委员会选择了这种行为?

为了让事情变得更简单,在 Coliru 上:

谢谢!

最佳答案

看看这个调用:

d.fun(foo_t{});

您创建一个临时(即右值),将其作为参数传递给函数。现在你认为会发生什么?

  • 它首先尝试绑定(bind)到参数Arg&& ,因为它可以接受右值 但是 由于无效的返回类型推导(这又是由于 foo_t 无法转换为 unsigned int ,因此 b.fun(std::forward<Args>(args)...) 原来是无效的表达式),如果您使用 decltype(expr),此功能将被拒绝作为返回类型,在这种情况下 SFINAE 就出现了。但是如果你简单地使用 auto , 则 SFINAE 不显示,该错误被归类为导致编译失败的硬错误。

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

关于c++ - 为什么自动返回类型会改变重载分辨率?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26710461/

相关文章:

c++ - 为什么 lambda 参数中不允许使用 auto?

c++ - 模板化函数指针作为模板参数

c++ - 用内存地址声明函数

c++ - 如何检测文件格式及其内容

c++ - 可用动态数组内存错误 : _BLOCK_TYPE_IS_VALID

c++ - 什么是 Apple GCC 中的 WSAAsyncSelect() 函数模拟?

c++ - 我可以使用从左到右的样式和 `auto` 来声明原始指针吗?

c++ - Visual Studio 与 G++ 中的 Decltype 和友元函数

c++ - 如果使用 WS_POPUP 样式创建 Window,则永远不会传递 WM_PAINT 消息

c++ - 为什么 boost::adaptors::filtered 的输出没有名为 size 的成员?