【发布时间】:2014-12-29 21:15:59
【问题描述】:
感谢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)...);
}
};
不等于这个(问题不在于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&,所以非常热切的decorator::fun 开始了(我们知道Args&&...非常不耐烦地绑定到任何不完全匹配的东西)。所以它将这个转发给无法处理foo_t的base::fun。
如果我将derived::fun 更改为foo_t 而不是const foo_t&,那么它会按预期工作,这表明确实这里的问题是derived::fun 和decorator::fun 之间存在竞争。
但是为什么这会显示返回类型的扣除???更准确地说,为什么委员会选择了这种行为?
为了让事情变得更简单,在 Coliru 上:
谢谢!
【问题讨论】:
-
@Nawaz:感谢您的提示。在这里,没有区别。
-
decltype(b.fun(std::forward<Args>(args)...))在尾随返回类型中用作 Expression SFINAE,而auto和decltype(auto)都不是 -
添加到 Piotr S. 的评论中:这也是故意的。要使 SFINAE 工作,返回类型必须在不查看函数体的情况下可用。要启用返回 lambda,返回类型必须在不查看函数体的情况下才可用。决定启用返回 lambda 比 SFINAE 更重要。
-
同意。我刚刚阅读了my own answer 并从中学习。 :|
标签: c++ auto c++14 return-type-deduction