所以我有一些“复杂的”代码,看起来像这样:
namespace details {
template <typename T>
T do_smth_impl (T&& v) {
// process
return std::move (v);
}
}
template <typename T>
T do_smth (T v) {
return details::do_smth_impl (std::move (v));
}
details::do_smth_impl()
采用转发参考,但是由于它位于details
命名空间中,并且仅用作实现细节(在这种情况下,不需要在其他功能中使用它,但是我确实需要因为有多个处理不同类型的do_smth_impl()
函数而将其分开),而且我确实确定只有rvalue引用将传递给impl函数,是否需要返回std::forward<T> (v)
?< / p>
答案 0 :(得分:6)
forward
。有一天,某人(也许是您!)会向do_smth_impl()
添加一个调用,并给它一个左值,然后该调用将编译,突然您的左值会从您的期望中移出。您将花一些时间对其进行调试,然后将其更改为forward
。写std::move
胜过std::forward<T>
根本没有好处(我认为较低的字符数没有好处)。
如果您真的打算将此函数仅接受rvalue,则应在其界面中使其明确。要么SFINAE:
template <typename T, std::enable_if_t<!std::is_reference<T>::value, int> = 0>
void do_smth_impl(T&&);
或static_assert
:
template <typename T>
void do_smth_impl(T&&) {
// there are lots of ways to spell this
static_assert(std::is_rvalue_reference<T&&>::value, "rvalues only");
static_assert(!std::is_lvalue_reference<T>::value, "!");
static_assert(!std::is_lvalue_reference<T&&>::value, "!");
static_assert(!std::is_reference<T>::value, "!");
}
这将使您的假设调用具有左值不编译。与编译和执行错误的操作相比,编译失败是更好的选择。然后,也许在那个呼叫站点,您可以花些时间认真考虑是否要向该函数传递左值。也许传递左值是一个错误,现在您抓住了它!
但是将功能保持不变,从转发引用中执行move
只是在麻烦。
答案 1 :(得分:0)
如果在很多地方需要这种要求,则可以使用下一种技术:
template <typename T, typename = typename std::enable_if<std::is_rvalue_reference<T&&>::value>::type>
using require_rvalue = T&&;
template <typename T>
void do_smth_impl(require_rvalue<T> val) {
...
}