模板化函数仅取rvalues

时间:2014-06-15 17:23:39

标签: c++ templates c++11 rvalue-reference

写一个只占左值的函数很容易

template <typename T>
void f(T&);

但由于universal references,写一个只接受右值引用的方法并不是直接的。有没有办法编写一个只接受rvalue refs的泛型函数? (返回类型不是真正的问题)

template <typename T>
void only_rvals(T&&);

,其中

int i{};
only_rvals(i); // fails to compile
only_rvals(6); // successfully compiles

2 个答案:

答案 0 :(得分:9)

添加

template<class T>
void only_rvals(T&)=delete;

普遍的过载。它将是首选,并在选择时生成错误。

或者,SFINAE检查,或后C ++ 1y要求条款,static_assert或使用is_reference或类似的标签发送都可以。

理想情况下,您希望在通话点失败,代码清晰,诊断清晰,解决方案的稳健性和可扩展性。后C ++ 1y要求最终将是最好的。 static_assert提供了明确的诊断(可以标记为disptching,但您希望调度is_rvalue的特性发送到true_type),SFINAE和=delete会在调用时发生错误现场。 =delete不能扩展到更多参数,SFINAE很脆弱。

SFINAE和requires允许使用替代重载。这可能是也可能不是。

答案 1 :(得分:4)

我可以看到两种可能的解决方案。第一个使用static_assert在编译时检查推断的类型是否是左值引用。如果是,则编译在该行上失败,并且您要添加任何有意义的错误

template <typename T>
void only_rvals(T&& t) {
    static_assert(!std::is_lvalue_reference<T>::value,
            "I only work with rvalues");
    // ...
}

如果您希望通过编译器“自然地”使用(imo)难以理解的错误而失败,则可以使用辅助函数(此处使用_impl)来执行此操作。被调用的函数使用remove_reference来调用辅助函数,其模板参数使用非引用类型。这可确保实例化的帮助程序具有其参数类型的右值引用。如果原始类型是左值引用,则调用将失败,因为尝试将参数作为左值引用转发。

#include <type_traits>
#include <utility>

template <typename T>
void only_rvals_impl(T&& t) {
    //...
}

template <typename T>
void only_rvals(T&& t) {
    // no type deduction on this call, explicit instantiation
    only_rvals_impl<typename std::remove_reference<T>::type>(
            std::forward<T>(t));
}

@ dyp使用SFINAE评论的第三个解决方案

template<typename T>
typename std::enable_if< !std::is_lvalue_reference<T>{}, void>::type
only_rvals(T&& t)  {
    //...
}

void中的enable_if是不必要的,因为它是默认设置,但此处显示的是易于修改的解决方案