我看到了std::forward
的3个实施方向。
为了测试它们,我写了流动的测试:
void test(int&&){ std::cout <<"refref\n";}
void test(int&){ std::cout <<"ref\n";}
template<class T>
void foo(T&& t){
test(fwd<T>(t));
int main (){
auto v = 5;
foo(5);
foo(v);
foo(std::move(v));
}
第一个也是最简单的是:
template <typename T>
T&& fwd(T& t) {
return static_cast<T&&>(t);
}
据我所知,这里的主要问题是T
可以推断出来。
在这种情况下,它将始终返回Rvalue引用(T
将推断为T
(永远不会T&
),并且不会有引用折叠)
为了解决这个问题,我们可以添加标识类:
template<typename T>
struct identity{
typedef T type;
};
template <typename T>
T&& fwd( typename identity<T>::type t) {
return static_cast<T&&>(t);
}
但是,我看到标准规定:
template <typename T>
T&& fwd( typename remove_reference<T>::type t) {
return static_cast<T&&>(t);
}
两者之间有什么区别吗?
答案 0 :(得分:0)
template <typename T>
T&& fwd( typename remove_reference<T>::type t) {
return static_cast<T&&>(t);
}
这不是std::forward
的正确实现。
template <typename T>
T&& fwd( typename remove_reference<T>::type& t) {
return static_cast<T&&>(t);
}
template <typename T>
T&& fwd( typename remove_reference<T>::type&& t) {
return static_cast<T&&>(t);
}
这两个是。
你的价值取t
; std::forward
始终采用右值或左值参考。 (我省略了c++14&#39; constexpr
,因为现在不重要。)
我会比较你的:
template <typename T>
T&& fwd( typename identity<T>::type t) {
return static_cast<T&&>(t);
}
到正确的代码。
你的错误有两种。首先,如果传入非引用类型,则按值获取:
struct foo {
foo(foo&&)=default;
foo(foo const&)=delete;
};
foo f1;
auto f2 = fwd<foo>(f1);
这在您的版本中无法编译,因为fwd<foo>
按值foo
获取,因此在调用它时会尝试将f1
复制到其参数中。
我们可以在某种程度上解决这个问题:
template <typename T>
T&& fwd( typename identity<T>::type&& t) {
return static_cast<T&&>(t);
}
但现在,当你传入T=foo
时,你被迫传递了一个右值。这不好。所以现在我们添加另一个重载:
template <typename T>
T&& fwd( typename identity<T>::type& t) {
return static_cast<T&&>(t);
}
当我们通过T=foo
时,我们可以使用 rvalue或左值参数。到现在为止还挺好。但是当我们通过T=foo&
时会发生什么?好吧,然后第一个重载变成foo& &&
变成foo&
,我们被阻止接受rvalues然后使用forward将它们转换为左值。
auto f3 = fwd<foo&>( foo{} );
上述内容对std::forward
是合法的,但不适用于fwd
。