在这样的代码中:
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
是否存在而向函数或其他函数发送一组参数,并且可以使用传递的参数调用,保持完整(我猜)它们的确切传递类型,以及何时返回类型为int
,与引用或const限定符无关。
我对这段代码有三个疑问。
(1)declval
返回类型仍然是通用引用吗?例如,带有declval<T>()
的{{1}},其返回类型为T = int&
(真正的r值引用)或int&&
,并再次推断为int & &&
传递给另一个电话时遵循通用参考的通常规则?我认为它没有(正如@ 101010所指出的那样),但在这种情况下我不知道如何进行完美的重载测试。
(2)我是否需要重新指定可变参数模板以使用通用引用推理规则,或者由于已经在(3)中推导出了正确的类型,它们是否保持其推导类型完整?
或者我可以写
int&
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
内实例化。
答案 0 :(得分:1)
这两种情况不等效。这个例子:
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)...); }