转发引用始终作为右值引用处理

Art*_*zuk 2 c++ c++11

所以我有一些看起来像这样的"复杂"代码:

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));
}
Run Code Online (Sandbox Code Playgroud)

details::do_smth_impl()采用转发引用,但因为它在details命名空间中并且仅用作实现细节(在这种情况下,不需要将它放在不同的函数中,但我真的需要将它分开,因为有多个do_smth_impl()函数可以处理不同的函数类型)我真的确定只有rvalue引用将被传递给impl函数是否需要返回std::forward<T> (v)?

Bar*_*rry 6

只是forward.有一天,有人(也许你!)将添加一个调用do_smth_impl()并给它一个左值,并且该调用将被编译,并且突然你的左值被移动而没有你期望它.你将花一些时间调试它,然后将其改为forward.有根本没有好处写std::move了std::forward<T>(我不考虑低位字符算一个好处).

如果你真的打算只接受rvalues 这个函数,那么你应该在它的接口中明确它.无论是SFINAE:

template <typename T, std::enable_if_t<!std::is_reference<T>::value, int> = 0>
void do_smth_impl(T&&);
Run Code Online (Sandbox Code Playgroud)

或者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, "!");
}
Run Code Online (Sandbox Code Playgroud)

这将使你的假设调用左值不编译.没有编译是比编译和做错事更好的选择.然后,也许,在该呼叫站点,您可以花时间并认真考虑是否将左值传递给此函数是否是您想要做的.也许通过左值是一个错误,现在你抓住了它!

但是,move从转发引用开始,保留该功能只是在寻找麻烦.