模板完美转发class
Perfect forwarding in a template class
我有一个模板 class,在该模板中 class 我有一个带有 universal/forwarding 参考参数的静态模板函数。这个想法是完善函数的前向参数。
#include <iostream>
#include <type_traits>
//Type traits
template <typename T>
struct is_lreference_const
{
static const bool value = std::is_lvalue_reference<T>::value && std::is_const<typename std::remove_reference<T>::type>::value;
};
template <typename T>
struct is_lvreference
{
static const bool value = std::is_lvalue_reference<T>::value && !std::is_const<typename std::remove_reference<T>::type>::value;
};
struct Bar{};
template <class... Args>
struct FooClass;
//Perfect forward to FooClass::impl()
template <class... T>
inline void foo(T&&... args) {
FooClass<T&&...>::impl(std::forward<T>(args)...);
}
template <typename T>
struct FooClass<T> {
inline static void impl(T&& b) {
if constexpr (is_lvreference<T>::value)
std::cout << "T&" << std::endl;
else if constexpr (is_lreference_const<T>::value)
std::cout << "const T&" << std::endl;
else if constexpr (std::is_rvalue_reference<T>::value)
std::cout << "T&&" << std::endl;
else
std::cout << "T" << std::endl;
}
};
int main()
{
const Bar b2;
foo(b2);
foo(Bar{});
Bar b;
foo(b);
}
一切正常,输出符合预期:
const T&
T&&
T&
但是,如果我要像这样更改 foo()
函数:
template <class... T>
inline void foo(T&&... args) {
FooClass<T...>::impl(std::forward<T>(args)...);
}
请注意 FooClass
class 没有获得转发参考。那么输出为:
const T&
T
T&
右值引用的值类别未传递给函数 impl()
,即使我正在使用 std::forward
。为什么会这样?是因为我没有在可推导的上下文中调用 impl()
,因为已经为 FooClass
推导了 T?如何避免此类问题?
(此问题与今天早些时候发布的另一个 相关)。
编辑
我把FooClass<T>::impl()
改成了模板函数。然后我发现 else if constexpr (std::is_rvalue_reference<T>::value)
从来都不是真的。我 discovered 这是因为右值被推断为 T
而不是 T&&
类型。所以我添加了一些打印功能,发现完美转发现在可以工作了:
#include <iostream>
#include <type_traits>
//Type traits
template <typename T>
struct is_lreference_const
{
static const bool value = std::is_lvalue_reference<T>::value && std::is_const<typename std::remove_reference<T>::type>::value;
};
template <typename T>
struct is_lvreference
{
static const bool value = std::is_lvalue_reference<T>::value && !std::is_const<typename std::remove_reference<T>::type>::value;
};
struct Bar{};
template <class... Args>
struct FooClass;
//Perfect forward to FooClass::impl()
template <class... T>
inline void foo(T&&... args) {
FooClass<T&&...>::impl(std::forward<T>(args)...);
}
template<typename T>
void printme(const T&) {
std::cout << "constant lvalue reference" << std::endl;
}
template<typename T>
void printme(T&) {
std::cout << "lvalue reference" << std::endl;
}
template<typename T>
void printme(T&&) {
std::cout << "rvalue reference" << std::endl;
}
template <typename T>
struct FooClass<T> {
template <typename Arg>
inline static void impl(Arg&& b) {
printme(std::forward<Arg>(b));
}
};
int main()
{
const Bar b2;
foo(b2);
foo(Bar{});
Bar b;
foo(b);
}
输出:
constant lvalue reference
rvalue reference
lvalue reference
template <typename T>
struct FooClass<T> {
inline static void impl(T&& b) {
FooClass<X>::impl
接受一个 X&&
,其中 T
是 X
.
如果X
是int&&
,那么T&&
就是int&& &&
,也就是int&&
,T
就是int&&
。
如果X
是int
,那么T&&
就是int&&
也就是int&&
,而T
就是int
.
在 impl
中,您查询 T
,而不是 b
。因此,如果您传递 FooClass
一个 int
或 int&&
,您将获得不同的值,即使 FooClass<X>::impl
的签名相同。
调用 impl
之外的 forward
对 impl
中的代码 零影响 ;要么调用成功,要么不成功,你会得到一个编译错误。
您不是推导参数impl
,您是在明确设置类型。
forward
只是一个条件 move
。这不是魔法。
您可能需要复习一下标准转发、模板函数参数推导(尤其是当您推导 T&&
参数时)以及引用折叠的工作原理。如果您不明白这一点,您可能会将 std forward 视为某种魔法,但事实并非如此。
我有一个模板 class,在该模板中 class 我有一个带有 universal/forwarding 参考参数的静态模板函数。这个想法是完善函数的前向参数。
#include <iostream>
#include <type_traits>
//Type traits
template <typename T>
struct is_lreference_const
{
static const bool value = std::is_lvalue_reference<T>::value && std::is_const<typename std::remove_reference<T>::type>::value;
};
template <typename T>
struct is_lvreference
{
static const bool value = std::is_lvalue_reference<T>::value && !std::is_const<typename std::remove_reference<T>::type>::value;
};
struct Bar{};
template <class... Args>
struct FooClass;
//Perfect forward to FooClass::impl()
template <class... T>
inline void foo(T&&... args) {
FooClass<T&&...>::impl(std::forward<T>(args)...);
}
template <typename T>
struct FooClass<T> {
inline static void impl(T&& b) {
if constexpr (is_lvreference<T>::value)
std::cout << "T&" << std::endl;
else if constexpr (is_lreference_const<T>::value)
std::cout << "const T&" << std::endl;
else if constexpr (std::is_rvalue_reference<T>::value)
std::cout << "T&&" << std::endl;
else
std::cout << "T" << std::endl;
}
};
int main()
{
const Bar b2;
foo(b2);
foo(Bar{});
Bar b;
foo(b);
}
一切正常,输出符合预期:
const T&
T&&
T&
但是,如果我要像这样更改 foo()
函数:
template <class... T>
inline void foo(T&&... args) {
FooClass<T...>::impl(std::forward<T>(args)...);
}
请注意 FooClass
class 没有获得转发参考。那么输出为:
const T&
T
T&
右值引用的值类别未传递给函数 impl()
,即使我正在使用 std::forward
。为什么会这样?是因为我没有在可推导的上下文中调用 impl()
,因为已经为 FooClass
推导了 T?如何避免此类问题?
(此问题与今天早些时候发布的另一个
编辑
我把FooClass<T>::impl()
改成了模板函数。然后我发现 else if constexpr (std::is_rvalue_reference<T>::value)
从来都不是真的。我 discovered 这是因为右值被推断为 T
而不是 T&&
类型。所以我添加了一些打印功能,发现完美转发现在可以工作了:
#include <iostream>
#include <type_traits>
//Type traits
template <typename T>
struct is_lreference_const
{
static const bool value = std::is_lvalue_reference<T>::value && std::is_const<typename std::remove_reference<T>::type>::value;
};
template <typename T>
struct is_lvreference
{
static const bool value = std::is_lvalue_reference<T>::value && !std::is_const<typename std::remove_reference<T>::type>::value;
};
struct Bar{};
template <class... Args>
struct FooClass;
//Perfect forward to FooClass::impl()
template <class... T>
inline void foo(T&&... args) {
FooClass<T&&...>::impl(std::forward<T>(args)...);
}
template<typename T>
void printme(const T&) {
std::cout << "constant lvalue reference" << std::endl;
}
template<typename T>
void printme(T&) {
std::cout << "lvalue reference" << std::endl;
}
template<typename T>
void printme(T&&) {
std::cout << "rvalue reference" << std::endl;
}
template <typename T>
struct FooClass<T> {
template <typename Arg>
inline static void impl(Arg&& b) {
printme(std::forward<Arg>(b));
}
};
int main()
{
const Bar b2;
foo(b2);
foo(Bar{});
Bar b;
foo(b);
}
输出:
constant lvalue reference
rvalue reference
lvalue reference
template <typename T>
struct FooClass<T> {
inline static void impl(T&& b) {
FooClass<X>::impl
接受一个 X&&
,其中 T
是 X
.
如果X
是int&&
,那么T&&
就是int&& &&
,也就是int&&
,T
就是int&&
。
如果X
是int
,那么T&&
就是int&&
也就是int&&
,而T
就是int
.
在 impl
中,您查询 T
,而不是 b
。因此,如果您传递 FooClass
一个 int
或 int&&
,您将获得不同的值,即使 FooClass<X>::impl
的签名相同。
调用 impl
之外的 forward
对 impl
中的代码 零影响 ;要么调用成功,要么不成功,你会得到一个编译错误。
您不是推导参数impl
,您是在明确设置类型。
forward
只是一个条件 move
。这不是魔法。
您可能需要复习一下标准转发、模板函数参数推导(尤其是当您推导 T&&
参数时)以及引用折叠的工作原理。如果您不明白这一点,您可能会将 std forward 视为某种魔法,但事实并非如此。