感谢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:
谢谢!
答案 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&
作为参数的第二个重载。