std :: initializer_list作为构造函数的模板参数

use*_*087 4 c++ constructor initializer-list c++11 type-deduction

考虑一个继承自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;
}
Run Code Online (Sandbox Code Playgroud)

如果我删除initializer_list构造函数vdi2将无法编译.所以我的问题:为什么初始化列表不是由模板构造函数推导出来的?这样可能吗?

Cas*_*eri 7

为什么initializer_list不是由模板化的构造函数推导出来的?

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

但是,通过特殊的标准规则std::initializer_list<T>(以及其他内容)可以从这个synctatic构造中构造并且T可以推导出来int.因此第二个构造函数工作.

通过与函数模板参数类型推导的约束,用

auto x = {1,2,3,4,5,6,7,8,9};
Run Code Online (Sandbox Code Playgroud)

编译器将x的类型设置为std::initializer_list<int>.还有一些特殊的标准规则,它必须如此.严格地说,这不是类型推断,因为如上所述,{1,2,3,4,5,6,7,8,9}没有要推断的类型.(唯一的类型推演这里发生的一切是T = intstd::initializer_list<T>.)这里的编译器选择(它并不推断)的类型xstd::initializer_list<int>.在任何情况下,使用滥用语言说明所x推断的类型是没有害处的std::initializer_list<int>.

最后,正如DyP在评论中所说,你可能想要的是从基础容器类继承所有构造函数(不仅仅是那些带有一个参数的构造函数).您可以通过删除当前拥有的所有构造函数来执行此操作,并将此行添加到test:

using container_type::container_type;
Run Code Online (Sandbox Code Playgroud)