编写仅采用左值的函数很容易
template <typename T>
void f(T&);
但是由于通用引用,编写只接受右值引用的引用并不简单。 有没有办法编写只接受右值引用的通用函数? (返回类型并不是真正的问题)
template <typename T>
void only_rvals(T&&);
哪里
int i{};
only_rvals(i); // fails to compile
only_rvals(6); // successfully compiles
添加一个
template<class T>
void only_rvals(T&)=delete;
超载到通用的。 它将是首选,如果选择则会生成错误。
或者,SFINAE 检查,或后 C++1y 要求子句,或
static_assert
,或使用 is_reference
或类似的标签调度都可以工作。
理想情况下,您希望调用时出现故障、代码清晰、诊断清晰、解决方案稳健且可扩展。 后 C++1y 的要求最终将是最好的。
static_assert
提供了清晰的诊断(与标记调度一样,但您希望将名为 is_rvalue
的特征分派到 true_type
),并且 SFINAE 和 =delete
使错误发生在调用站点。 =delete
无法扩展到更多参数,SFINAE 很脆弱。
SFINAE 和
requires
允许使用替代重载。 这可能是所希望的,也可能不是。
我可以看到两种可能的解决方案。 第一个使用
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
是不必要的,因为它是默认值,但此处显示是为了易于修改的解决方案
- 如果 P 是对 cv-unqualified
的右值引用
记下此要求和标准中的示例代码。 您所需要的只是“假”const/volatile T&&
template <typename T>
void only_rvals(volatile T&&);