5

考虑一个从 std 容器继承的类,该类具有调用容器底层构造函数的模板构造函数。此模板构造函数适用于简单的复制和移动构造函数,但不适用于 initializer_list ctor。

template<typename container_T>
class test : public container_T {
public:
  using container_type = container_T;

  test() {} 

  // templated constructor
  template<typename T>
  test(T t)
    : container_T(t) {}

   // without this it won't compile
  test(std::initializer_list<typename container_T::value_type> l)
    : container_T(l) {}
};

int main() {    
  test<std::deque<int>> vdi1;
  test<std::deque<int>> vdi2({1,2,3,4,5,6,7,8,9});

  std::cout << "vdi2 before:" << std::endl;
  for(auto it : vdi2)
    std::cout << it << std::endl;

  test<std::deque<int>> vdi3(std::move(vdi2));

  std::cout << "vdi2 before:" << std::endl;
  for(auto it : vdi2)
    std::cout << it << std::endl;

  std::cout << "vdi3 before:" << std::endl;
  for(auto it : vdi3)
    std::cout << it << std::endl;

  return 0;
}

如果我删除initializer_list构造函数vdi2将无法编译。所以我的问题是:为什么模板构造函数没有推断出initializer_list?有可能这样做吗?

4

1 回答 1

8

为什么初始化器列表不是由模板化构造函数推导出来的?

原因是这{1,2,3,4,5,6,7,8,9}只是一个没有类型的合体结构。因此,编译器无法推断T此同步构造的类型,并且第一个构造函数失败。

然而,通过特殊的标准规则std::initializer_list<T>(除其他外)可以从这个合体构造构造并且T可以推导出为int. 因此第二个构造函数起作用。

通过与函数模板参数类型推导对比,与

auto x = {1,2,3,4,5,6,7,8,9};

编译器将 x 的类型设置为std::initializer_list<int>。还有一些特殊的标准规则规定必须如此。严格来说,这不是类型推导,因为如上所述,{1,2,3,4,5,6,7,8,9}没有要推导的类型。(这里发生的唯一类型推导是T = intin std::initializer_list<T>。)这里编译器选择(它不推导)xto的类型std::initializer_list<int>。无论如何,滥用语言的类型x推断为 是没有害处的std::initializer_list<int>

Finally, as DyP said in the comments, what you probably want is inheriting all constructors (not only those taking one argument) from the base container class. You can do this by removing all the constructors that you currently have and add just this line to test:

using container_type::container_type;
于 2013-10-21T10:34:19.030 回答