变量函数模板,包扩展不在最后一个参数中

HC4*_*ica 15 c++ templates function-templates variadic-templates c++11

我想知道为什么以下代码无法编译:

struct S
{
    template <typename... T>
    S(T..., int);
};

S c{0, 0};
Run Code Online (Sandbox Code Playgroud)

此代码无法使用clang和GCC 4.8进行编译.这是clang的错误:

test.cpp:7:3: error: no matching constructor for initialization of 'S'
S c{0, 0};
  ^~~~~~~
test.cpp:4:5: note: candidate constructor not viable: requires 1 argument, but 2 were provided
    S(T..., int);
    ^
Run Code Online (Sandbox Code Playgroud)

在我看来,这应该工作,T应该被推断为一个长度为1的包.

如果标准禁止做这样的事情,有谁知道为什么?

Joh*_*itb 8

因为当函数参数包不是最后一个参数时,模板参数包不能从它推导出来,并且模板参数推导将忽略它.

因此,将两个论点0, 0进行比较, int,产生不匹配.

像这样的扣除规则需要涵盖许多特殊情况(例如当两个参数包彼此相邻时会发生什么).由于参数包是C++ 11中的新功能,因此相应提案的作者保守地起草了规则.

请注意,如果没有以其他方式推断,则尾随模板参数包将为空.事情当你用一个参数调用构造函数时,事情就会起作用(注意模板参数包和函数参数包的不同之处.前者是尾随的,后者不是).

  • 为什么忽略它,而不是导致编译器错误? (2认同)

Yak*_*ont 6

所以,应该有一个解决方法.这些方面的东西:

namespace v1 {
  // Extract the last type in a parameter pack.
  // 0, the empty pack has no last type (only called if 1 and 2+ don't match)
  template<typename... Ts>
  struct last_type {};

  // 2+ in pack, recurse:
  template<typename T0, typename T1, typename... Ts>
  struct last_type<T0, T1, Ts...>:last_type<T1, Ts...>{};

  // Length 1, last type is only type:
  template<typename T0>
  struct last_type<T0> {
    typedef T0 type;
  };
}
namespace v2 {
  template<class T> struct tag_t{using type=T;};
  template<class T> using type_t = typename T::type;
  template<class...Ts>
  using last = type_t< std::tuple_element_t< sizeof...(Ts)-1, std::tuple<tag_t<Ts>...> > >;
  template<class...Ts>
  struct last_type {
    using type=last<Ts...>;
  };
}
template<class...Ts>
using last_type=v2::late_type<Ts...>; // or v1   


struct S
{
    // We accept any number of arguments
    // So long as the type of the last argument is an int
    // probably needs some std::decay to work right (ie, to implicitly work out that
    // the last argument is an int, and not a const int& or whatever)
    template <typename... T, typename=typename std::enable_if<std::is_same<int, typename last_type<T...>::type>>::type>
    S(T...);

};
Run Code Online (Sandbox Code Playgroud)

我们检查参数包的最后一种类型是一个int,还是我们只传递了一个参数包的类型int.