如何在成员函数中完美转发`*this`对象
How to perfectly forward `*this` object inside member function
是否可以在成员函数中完美转发*this
对象?如果是,那我们该怎么做呢?如果不是,那为什么不呢,我们有什么替代方法可以达到同样的效果。
请查看下面的代码片段以更好地理解问题。
class Experiment {
public:
double i, j;
Experiment(double p_i = 0, double p_j = 0) : i(p_i), j(p_j) {}
double sum() { return i + j + someConstant(); }
double someConstant() && { return 10; }
double someConstant() & { return 100; }
};
int main() {
Experiment E(3, 5);
std::cout << std::move(E).sum() << "\n"; // prints: 108
std::cout << E.sum() << "\n"; // prints: 108
}
如果我们认为成员函数 double sum()
中的 *this
对象始终是左值或 xvalue(因此是泛左值),则此输出似乎是预期的。请确认这是否属实。
如何才能完美转发*this
对象到double sum()
成员函数内部的成员函数调用someConstant()
?
我尝试使用 std::forward
如下:
double sum() {
return i + j + std::forward<decltype(*this)>(*this).someConstant();
}
但这没有任何效果,double someConstant() &
重载总是被调用。
如果不为 &
和 &&
限定符重载 sum
,这在 C++11 中是不可能的。 (在这种情况下,您可以根据特定重载的限定符确定值类别。)
*this
就像任何间接的结果一样,是一个左值,也是调用隐式成员函数调用的对象。
这将在 C++23 中通过引入可以应用常规转发的显式对象参数来解决:http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p0847r7.html
人们会认为 std::forward()
会保留左值引用,但它不会在 non-template 上下文中,如下例所示。
call_f()&
和 call_f()&&
都调用了 f()&&
。 std::forward<Experiment>(*this)
在 non-template 函数中 returns 右值引用,与参数的值类别无关。
请注意这与模板函数有何不同,无论是否为成员(我将成员函数设为静态,因为它接收“this
引用”作为显式参数)。两个都“正确地”转发左值引用(最后 4 次调用)。
#include<iostream>
#include<utility>
#include<string>
struct Experiment
{
public:
std::string f()&& { return "f()&&"; }
std::string f()& { return "f()&"; }
std::string call_f()&& { std::cout << "call_f()&& "; return std::forward<Experiment>(*this).f(); }
// I need this function because it is not a template function
std::string call_f()& { std::cout << "call_f()& "; return std::forward<Experiment>(*this).f(); }
template<class T = Experiment>
static std::string E_t_call_f(T&& t) { std::cout << "E_t_call_f(T&& t) "; return std::forward<T>(t).f(); }
};
template<class T>
std::string t_call_f(T&& t) { std::cout << "t_call_f(T&& t) "; return std::forward<T>(t).f(); }
int main()
{
Experiment E;
std::cout << "E.f(): " << E.f() << '\n';
std::cout << "move(E).f(): " << std::move(E).f() << '\n';
std::cout << '\n';
std::cout << "E.call_f(): " << E.call_f() << '\n';
std::cout << "move(E).call_f(): " << std::move(E).call_f() << '\n';
std::cout << '\n';
std::cout << "t_call_f(E): " << t_call_f(E) << '\n';
std::cout << "t_call_f(std::move(E)): " << t_call_f(std::move(E)) << '\n';
std::cout << '\n';
std::cout << "E::E_t_call_f(E): " << Experiment::E_t_call_f(E) << '\n';
std::cout << "E::E_t_call_f(std::move(E)): " << Experiment::E_t_call_f(std::move(E)) << '\n';
}
在结果输出中,第三行令人惊讶:std::forward<Experiment>(*this)
的类型 *this
的左值引用是右值引用。
E.f(): f()&
move(E).f(): f()&&
call_f()& E.call_f(): f()&&
call_f()&& move(E).call_f(): f()&&
t_call_f(T&& t) t_call_f(E): f()&
t_call_f(T&& t) t_call_f(std::move(E)): f()&&
E_t_call_f(T&& t) E::E_t_call_f(E): f()&
E_t_call_f(T&& t) E::E_t_call_f(std::move(E)): f()&&
是否可以在成员函数中完美转发*this
对象?如果是,那我们该怎么做呢?如果不是,那为什么不呢,我们有什么替代方法可以达到同样的效果。
请查看下面的代码片段以更好地理解问题。
class Experiment {
public:
double i, j;
Experiment(double p_i = 0, double p_j = 0) : i(p_i), j(p_j) {}
double sum() { return i + j + someConstant(); }
double someConstant() && { return 10; }
double someConstant() & { return 100; }
};
int main() {
Experiment E(3, 5);
std::cout << std::move(E).sum() << "\n"; // prints: 108
std::cout << E.sum() << "\n"; // prints: 108
}
如果我们认为成员函数 double sum()
中的 *this
对象始终是左值或 xvalue(因此是泛左值),则此输出似乎是预期的。请确认这是否属实。
如何才能完美转发*this
对象到double sum()
成员函数内部的成员函数调用someConstant()
?
我尝试使用 std::forward
如下:
double sum() {
return i + j + std::forward<decltype(*this)>(*this).someConstant();
}
但这没有任何效果,double someConstant() &
重载总是被调用。
如果不为 &
和 &&
限定符重载 sum
,这在 C++11 中是不可能的。 (在这种情况下,您可以根据特定重载的限定符确定值类别。)
*this
就像任何间接的结果一样,是一个左值,也是调用隐式成员函数调用的对象。
这将在 C++23 中通过引入可以应用常规转发的显式对象参数来解决:http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p0847r7.html
人们会认为 std::forward()
会保留左值引用,但它不会在 non-template 上下文中,如下例所示。
call_f()&
和 call_f()&&
都调用了 f()&&
。 std::forward<Experiment>(*this)
在 non-template 函数中 returns 右值引用,与参数的值类别无关。
请注意这与模板函数有何不同,无论是否为成员(我将成员函数设为静态,因为它接收“this
引用”作为显式参数)。两个都“正确地”转发左值引用(最后 4 次调用)。
#include<iostream>
#include<utility>
#include<string>
struct Experiment
{
public:
std::string f()&& { return "f()&&"; }
std::string f()& { return "f()&"; }
std::string call_f()&& { std::cout << "call_f()&& "; return std::forward<Experiment>(*this).f(); }
// I need this function because it is not a template function
std::string call_f()& { std::cout << "call_f()& "; return std::forward<Experiment>(*this).f(); }
template<class T = Experiment>
static std::string E_t_call_f(T&& t) { std::cout << "E_t_call_f(T&& t) "; return std::forward<T>(t).f(); }
};
template<class T>
std::string t_call_f(T&& t) { std::cout << "t_call_f(T&& t) "; return std::forward<T>(t).f(); }
int main()
{
Experiment E;
std::cout << "E.f(): " << E.f() << '\n';
std::cout << "move(E).f(): " << std::move(E).f() << '\n';
std::cout << '\n';
std::cout << "E.call_f(): " << E.call_f() << '\n';
std::cout << "move(E).call_f(): " << std::move(E).call_f() << '\n';
std::cout << '\n';
std::cout << "t_call_f(E): " << t_call_f(E) << '\n';
std::cout << "t_call_f(std::move(E)): " << t_call_f(std::move(E)) << '\n';
std::cout << '\n';
std::cout << "E::E_t_call_f(E): " << Experiment::E_t_call_f(E) << '\n';
std::cout << "E::E_t_call_f(std::move(E)): " << Experiment::E_t_call_f(std::move(E)) << '\n';
}
在结果输出中,第三行令人惊讶:std::forward<Experiment>(*this)
的类型 *this
的左值引用是右值引用。
E.f(): f()&
move(E).f(): f()&&
call_f()& E.call_f(): f()&&
call_f()&& move(E).call_f(): f()&&
t_call_f(T&& t) t_call_f(E): f()&
t_call_f(T&& t) t_call_f(std::move(E)): f()&&
E_t_call_f(T&& t) E::E_t_call_f(E): f()&
E_t_call_f(T&& t) E::E_t_call_f(std::move(E)): f()&&