如何使用类型特征将函数的通用引用参数限制为 r 值引用?

How can I use type traits to limit the universal reference parameters of a function to r-value references?

本文关键字:引用 参数 特征 类型 何使用 函数      更新时间:2023-10-16

我想定义一个具有通用引用参数的函数op2()

根据类型,我想进一步区分op2()中的函数参数是否是类型test的 r 值引用。 如果是这种情况,我想将op2的返回类型定义为int。 在所有其他情况下,返回类型应为void

这是我的我:

#include <type_traits>
#include <iostream>
#include <string>
class test {
public:
test(const std::string& str) : _str(str){}
test(test&& t) = default;
test(const test& t) = default;
friend std::ostream& operator<<(std::ostream& os, test&& t) {
os << t._str;
return os;
}
private:
std::string _str;
};
template<typename T>
void op(T&& x) {
std::cout << " is rvalue ref? " << std::is_rvalue_reference<decltype(x)>::value << std::endl;
std::cout << " is a test rvalue ref? " << std::is_same<test&&, decltype(x)>::value << std::endl;
std::cout << std::forward<T>(x) << std::endl;
}
template<typename T>        // This is A
typename std::enable_if<std::negation<std::conjunction<std::is_same<test, T>, std::is_rvalue_reference<T>>>::value>::type op2(T&& x) {
std::cout << "op2: A: " << std::forward<T>(x) << std::endl;
}
template<typename T>    // This is B
typename std::enable_if<std::conjunction<std::is_same<test, T>, std::is_rvalue_reference<T>>::value, int>::type op2(T&& x) {
std::cout << "op2: B: " << std::move(x) << std::endl;
return EXIT_SUCCESS;
}
int main() {
op(std::string{"r-value string"});
std::string str{"l-value string"};
op(str);
op(test{"test"});
op2(std::string{"r-value string"}); //gets into A
op2(str); //gets into A
op2(test{"r-value string"}); //Should get into B, but actually gets into A
}

代码的问题在于,最后一个op2()调用进入错误的重载。

我尝试了另一个函数,op()它通过decltype()获得正确的类型,但我不知道如何在类型特征中使用decltype()

我正在使用 C++17 和 gcc8.2

转发引用T&&永远不会将其模板参数T推断为右值引用类型。如果参数是类型为test的右值,则T将被推导出为test,而如果参数是test类型的左值,则T将被推导为test&

因此,重载应重写为:

template<typename T>
std::enable_if_t<!std::is_same_v<test, T>, void> op2(T&& x) {
std::cout << "op2: A: " << std::forward<T>(x) << std::endl;
}
template<typename T>
std::enable_if_t<std::is_same_v<test, T>, int> op2(T&& x) {
std::cout << "op2: B: " << std::move(x) << std::endl;
return EXIT_SUCCESS;
}

要在只有类型时使用decltype,您必须使用std::declval

decltype(std::declval<T>())

但是你总是会从中获得一个右值引用,所以这不是你想要的。相反,请利用转发引用推导的类型。 如果传入右值,T将不是引用,并且将是左值的T&

这意味着您可以将 B 修改为:

template<typename T>
std::enable_if_t<std::is_same_v<test, T>, int> op2(T&& x);

因为如前所述,如果您传入类型test的右值,T将被推导出为test

在您的情况下,简单的重载更简单:

template<typename T>
void op2(T&& x) { // This is A
std::cout << "op2: A: " << std::forward<T>(x) << std::endl;
}
int op2(test&& x) { // This is B
std::cout << "op2: B: " << std::move(x) << std::endl;
return EXIT_SUCCESS;
}

演示