Class 模板实例化和通用引用
Class template instantiations and universal references
在这样的代码中:
template<class...> struct pack{};
template<class, class = int>
struct call_t
{
template<class... args_t>
static int apply(args_t&&...)
{ return 0; }
};
template<class... args_t>
struct call_t<pack<args_t...>, // (1)
std::decay_t<decltype(convert(declval<args_t>()...))> >
{
template<class... params_t> // (2)
static int apply(params_t&&... args)
{ return convert(std::forward<params_t>(args)...); }
};
template<class... args_t>
auto test(args_t&&... args) // (3)
{
return call_t<pack<args_t...> >::
apply(std::forward<args_t>(args)...);
}
这个函数根据一个函数 convert
是否存在并且可以用传递的参数调用,保持完整(我猜)它们的确切传递类型,以及何时它的 return 类型是 int
,与引用或 const 限定符无关。
我对那段代码有3个疑惑
(1) declval
return 类型仍然是通用引用吗?例如,declval<T>()
和 T = int&
的 return 类型是 int&&
(真正的 r 值引用),还是 int & &&
并再次推导为int&
在传递给另一个调用时遵循通用引用的通常规则?我认为它没有(正如@101010 所指出的),但在那种情况下我不知道如何进行完美的过载测试。
(2) 我是否需要重新指定可变参数模板以使用通用引用推导规则,或者由于在 (3) 中已经推导了正确的类型,它们保留了推导的类型完好无损?
或者我可以只写
template<class... args_t>
struct call_t<pack<args_t...>, // (1)
std::decay_t<decltype(convert(declval<args_t>()...))> >
{
// (2)
static int apply(args_t... args)
{ return convert(args...); }
};
?
call_t
模板 class 是一个实现细节,因此,它只会在 test
内部实例化。
这两种情况不等同。这个例子:
template<class... args_t>
struct call_t<pack<args_t...>, // (1)
std::decay_t<decltype(convert(declval<args_t>()...))> >
{
// (2)
static int apply(args_t... args)
{ return convert(args...); }
};
不转发任何东西。参数包 args...
是左值,因为它们有名称。这与这段代码非常不同:
template<class... params_t> // (2)
static int apply(params_t&&... args)
{ return convert(std::forward<params_t>(args)...); }
其中args...
被转发。由此产生的行为是新示例可能比旧示例慢(执行复制而不是移动)或者可能会出人意料地无法编译。考虑 convert(std::unique_ptr<int> )
。 是 可通过 args_t = {std::unique_ptr<int>}
调用,但内部 apply()
会失败,因为您将尝试复制 unique_ptr
.
你需要做的:
static int apply(args_t... args)
{ return convert(std::forward<args_t>(args)...); }
在这样的代码中:
template<class...> struct pack{};
template<class, class = int>
struct call_t
{
template<class... args_t>
static int apply(args_t&&...)
{ return 0; }
};
template<class... args_t>
struct call_t<pack<args_t...>, // (1)
std::decay_t<decltype(convert(declval<args_t>()...))> >
{
template<class... params_t> // (2)
static int apply(params_t&&... args)
{ return convert(std::forward<params_t>(args)...); }
};
template<class... args_t>
auto test(args_t&&... args) // (3)
{
return call_t<pack<args_t...> >::
apply(std::forward<args_t>(args)...);
}
这个函数根据一个函数 convert
是否存在并且可以用传递的参数调用,保持完整(我猜)它们的确切传递类型,以及何时它的 return 类型是 int
,与引用或 const 限定符无关。
我对那段代码有3个疑惑
(1)
declval
return 类型仍然是通用引用吗?例如,declval<T>()
和T = int&
的 return 类型是int&&
(真正的 r 值引用),还是int & &&
并再次推导为int&
在传递给另一个调用时遵循通用引用的通常规则?我认为它没有(正如@101010 所指出的),但在那种情况下我不知道如何进行完美的过载测试。(2) 我是否需要重新指定可变参数模板以使用通用引用推导规则,或者由于在 (3) 中已经推导了正确的类型,它们保留了推导的类型完好无损?
或者我可以只写
template<class... args_t>
struct call_t<pack<args_t...>, // (1)
std::decay_t<decltype(convert(declval<args_t>()...))> >
{
// (2)
static int apply(args_t... args)
{ return convert(args...); }
};
?
call_t
模板 class 是一个实现细节,因此,它只会在 test
内部实例化。
这两种情况不等同。这个例子:
template<class... args_t>
struct call_t<pack<args_t...>, // (1)
std::decay_t<decltype(convert(declval<args_t>()...))> >
{
// (2)
static int apply(args_t... args)
{ return convert(args...); }
};
不转发任何东西。参数包 args...
是左值,因为它们有名称。这与这段代码非常不同:
template<class... params_t> // (2)
static int apply(params_t&&... args)
{ return convert(std::forward<params_t>(args)...); }
其中args...
被转发。由此产生的行为是新示例可能比旧示例慢(执行复制而不是移动)或者可能会出人意料地无法编译。考虑 convert(std::unique_ptr<int> )
。 是 可通过 args_t = {std::unique_ptr<int>}
调用,但内部 apply()
会失败,因为您将尝试复制 unique_ptr
.
你需要做的:
static int apply(args_t... args)
{ return convert(std::forward<args_t>(args)...); }