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

std::initializer_list as template argument for constructor

本文关键字:参数 构造函数 initializer list std      更新时间:2023-10-16

考虑一个从 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?有可能这样做吗?

为什么模板化构造函数不推导initializer_list?

原因是{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}没有要推导的类型。(这里发生的唯一类型推导是std::initializer_list<T>中的T = int。在这里,编译器选择(它不推断)要std::initializer_list<int>x类型。无论如何,使用滥用语言说x的类型被推断为std::initializer_list<int>是没有害处的。

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

using container_type::container_type;