具有模板化的通用参考 class
Universal reference with templated class
示例:
template <typename T>
class Bar
{
public:
void foo(T&& arg)
{
std::forward<T>(arg);
}
};
Bar<int> bar;
bar.foo(10); // works
int a{ 10 };
bar.foo(a); // error C2664: cannot convert argument 1 from 'int' to 'int &&'
似乎通用引用 仅 具有模板函数而 仅 具有类型推导,对吧?所以用 class 没有意义吗?在我的情况下使用 std::forward
有意义吗?
你是对的:"universal references" 仅在 推导的 参数的类型为 T&&
时出现。在你的情况下,没有推论(T
从 class 中得知),因此没有通用参考。
在您的代码段中,std::forward
将始终执行 std::move
,因为 arg
是常规右值引用。
如果你想生成一个通用引用,你需要foo
一个函数模板:
template <typename T>
class Bar
{
public:
template <typename U>
void foo(U&& arg)
{
std::forward<U>(arg);
}
};
请注意,首选术语(即将出现在规范的未来版本中的术语)现在是 转发参考。
如您所说,转发引用仅适用于函数模板中的类型推导。在你的例子中,当你说 T&&
时,T
是 int
。它不能是 int&
因为它已在您的 Bar
实例化中明确说明。因此,不会出现引用折叠规则,因此您无法进行完美转发。
如果你想在成员函数中做完美的转发,你需要有一个成员函数模板:
template <typename U>
void foo(U&& arg)
{
std::forward<U>(arg); //actually do something here
}
如果您绝对需要 U
具有与 T
相同的非限定类型,您可以执行 static_assert
:
template <typename U>
void foo(U&& arg)
{
static_assert(std::is_same<std::decay_t<U>,std::decay_t<T>>::value,
"U must be the same as T");
std::forward<U>(arg); //actually do something here
}
std::decay
对您来说可能有点过于激进,因为它会将数组类型衰减为指针。如果这不是您想要的,您可以编写自己的简单特征:
template <typename T>
using remove_cv_ref = std::remove_cv_t<std::remove_reference_t<T>>;
template <typename T, typename U>
using is_equiv = std::is_same<remove_cv_ref<T>, remove_cv_ref<U>>;
如果你需要可变版本,我们可以写一个 are_equiv
trait。首先,我们需要一个特征来检查包中的所有特征是否为真。我将使用 bool_pack
方法:
namespace detail
{
template<bool...> struct bool_pack;
template<bool... bs>
using all_true = std::is_same<bool_pack<bs..., true>, bool_pack<true, bs...>>;
}
template <typename... Ts>
using all_true = detail::all_true<Ts::value...>;
然后我们需要检查 Ts...
和 Us...
中的每一对类型是否满足 is_equiv
。我们不能将两个参数包作为模板参数,所以我将使用 std::tuple 来分隔它们(您可以使用哨兵节点,或者如果需要,可以在中途拆分包):
template <typename TTuple, typename UTuple>
struct are_equiv;
template <typename... Ts, typename... Us>
struct are_equiv <std::tuple<Ts...>, std::tuple<Us...>> : all_true<is_equiv<Ts,Us>...>
{};
然后我们可以这样使用:
static_assert(are_equiv<std::tuple<Ts...>,std::tuple<Us...>>::value,
"Us must be equivalent to Ts");
示例:
template <typename T>
class Bar
{
public:
void foo(T&& arg)
{
std::forward<T>(arg);
}
};
Bar<int> bar;
bar.foo(10); // works
int a{ 10 };
bar.foo(a); // error C2664: cannot convert argument 1 from 'int' to 'int &&'
似乎通用引用 仅 具有模板函数而 仅 具有类型推导,对吧?所以用 class 没有意义吗?在我的情况下使用 std::forward
有意义吗?
你是对的:"universal references" 仅在 推导的 参数的类型为 T&&
时出现。在你的情况下,没有推论(T
从 class 中得知),因此没有通用参考。
在您的代码段中,std::forward
将始终执行 std::move
,因为 arg
是常规右值引用。
如果你想生成一个通用引用,你需要foo
一个函数模板:
template <typename T>
class Bar
{
public:
template <typename U>
void foo(U&& arg)
{
std::forward<U>(arg);
}
};
请注意,首选术语(即将出现在规范的未来版本中的术语)现在是 转发参考。
如您所说,转发引用仅适用于函数模板中的类型推导。在你的例子中,当你说 T&&
时,T
是 int
。它不能是 int&
因为它已在您的 Bar
实例化中明确说明。因此,不会出现引用折叠规则,因此您无法进行完美转发。
如果你想在成员函数中做完美的转发,你需要有一个成员函数模板:
template <typename U>
void foo(U&& arg)
{
std::forward<U>(arg); //actually do something here
}
如果您绝对需要 U
具有与 T
相同的非限定类型,您可以执行 static_assert
:
template <typename U>
void foo(U&& arg)
{
static_assert(std::is_same<std::decay_t<U>,std::decay_t<T>>::value,
"U must be the same as T");
std::forward<U>(arg); //actually do something here
}
std::decay
对您来说可能有点过于激进,因为它会将数组类型衰减为指针。如果这不是您想要的,您可以编写自己的简单特征:
template <typename T>
using remove_cv_ref = std::remove_cv_t<std::remove_reference_t<T>>;
template <typename T, typename U>
using is_equiv = std::is_same<remove_cv_ref<T>, remove_cv_ref<U>>;
如果你需要可变版本,我们可以写一个 are_equiv
trait。首先,我们需要一个特征来检查包中的所有特征是否为真。我将使用 bool_pack
方法:
namespace detail
{
template<bool...> struct bool_pack;
template<bool... bs>
using all_true = std::is_same<bool_pack<bs..., true>, bool_pack<true, bs...>>;
}
template <typename... Ts>
using all_true = detail::all_true<Ts::value...>;
然后我们需要检查 Ts...
和 Us...
中的每一对类型是否满足 is_equiv
。我们不能将两个参数包作为模板参数,所以我将使用 std::tuple 来分隔它们(您可以使用哨兵节点,或者如果需要,可以在中途拆分包):
template <typename TTuple, typename UTuple>
struct are_equiv;
template <typename... Ts, typename... Us>
struct are_equiv <std::tuple<Ts...>, std::tuple<Us...>> : all_true<is_equiv<Ts,Us>...>
{};
然后我们可以这样使用:
static_assert(are_equiv<std::tuple<Ts...>,std::tuple<Us...>>::value,
"Us must be equivalent to Ts");