类模板实例化和通用引用

时间:2017-04-11 00:40:44

标签: c++ templates c++14 forwarding-reference

在这样的代码中:

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内实例化。

1 个答案:

答案 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)...); }