【发布时间】:2021-01-02 05:40:38
【问题描述】:
我有一个模板类,在该类中我有一个带有通用/转发引用参数的静态模板函数。这个想法是完善函数的前向参数。
#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 类没有获得转发引用。那么输出是:
const T&
T
T&
即使我使用的是std::forward,右值引用的值类别也不会传递给函数impl()。为什么会这样?是因为我没有在可推断的上下文中调用impl(),因为已经为FooClass 推断了T?如何避免这样的问题?
(这个问题与今天早些时候发布的另一个 one 有关)。
编辑
我将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
【问题讨论】:
-
T&&inimpl(T&& b)不是转发引用,它是右值引用。获得转发引用的唯一方法是使impl成为模板本身。 -
@Evg 但是
impl()是一个模板函数。 -
impl()在您的示例中不是模板,它是类模板的非模板成员函数。 -
只有功能模板支持完美转发。如果函数前面没有
template<...>,那么你使用的模板参数在实例化类时就已经知道了,所以没有完美的转发。
标签: c++ templates perfect-forwarding