当前位置: 首页 > 知识库问答 >
问题:

对于构造函数,如何在可变模板与std::initializer\u列表之间进行选择?

曾泳
2023-03-14

在c 11的当前状态下(比如gcc 4.7.2),当我需要一个可以接受变量参数的构造函数时,我应该如何选择使用变量模板还是使用std::initializer\u list?

共有3个答案

朱浩大
2023-03-14

我建议始终选择可变参数模板,并尽可能避免std::initializer_list

这就是我用C 11实现std::向量的方式:

#include <iostream>
#include <vector>

struct exp_sequence {
  template <typename... T>
  exp_sequence(T&&...) {}
};

struct from_arglist_t {} from_arglist;

template <typename T>
class my_vector {
  std::vector<T> data;

public:
  my_vector(int n, T const& e) : data(n, e) {}

  template <typename... Args>
  my_vector(from_arglist_t, Args&&... args) {
    data.reserve(sizeof...(Args));
    exp_sequence{(data.push_back(std::forward<Args>(args)),1)...};
  }

  std::size_t size() { return data.size(); }
};

int main()
{
  std::vector<int> v1{13, 13}; std::cout << v1.size() << '\n'; // 2
  std::vector<int> v2(13, 13); std::cout << v2.size() << '\n'; // 13

  my_vector<int> v3{13, 13}; std::cout << v3.size() << '\n'; // 13
  my_vector<int> v4(13, 13); std::cout << v4.size() << '\n'; // 13
  my_vector<int> v5(from_arglist, 13, 13); std::cout << v5.size() << '\n'; // 2
  my_vector<int> v6{from_arglist, 13, 13}; std::cout << v6.size() << '\n'; // 2
}

原因如main所示,在泛型代码中使用initializer_list可能会导致不同的行为,具体取决于选择的括号类型。也可以通过添加这样的构造函数来默默更改代码。

另一个原因是仅移动类型:

//std::vector<move_only> m1{move_only{}}; // won't compile
my_vector<move_only> m2{from_arglist, move_only{}}; // works fine
秋博容
2023-03-14

使用可变参数模板,参数的数量在编译期间是已知的(并且可以通过sizeof...访问)。使用std::initializer_list,参数的数量只有在运行时才知道。因此,部分决策取决于您何时需要或想知道您有多少参数

长孙诚
2023-03-14

变量模板允许您提供不同类型的参数,而std::initializer\u list则以参数的类型为模板。这意味着列表中所有元素的类型必须相同(或可转换为基础类型,但不允许缩小转换)。根据这是否适合您,您可以选择其中之一。

此外,如果需要完美转发,可变模板通常是默认选择,因为语法形式

struct A
{
    // Deduces T& for lvalue references, T for rvalue references, and binds to both
    template<typename... Ts>
    A(Ts&&...) { }

    // This is an rvalue reference to an initializer_list. The above type deduction
    // does not apply here
    template<typename T>
    A(initializer_list<T>&&) { }
};

另外请注意,当您使用统一初始化语法(即大括号)时,默认情况下将调用接受初始值设定项列表的构造函数,即使存在另一个可行的构造函数。这可能是您想要的,也可能不是您想要的:

struct A
{
    A(int i) { }
};

struct B
{
    B(int) { }
    B(std::initializer_list<A>) { }
};

int main()
{
    B b {1}; // Will invoke the constructor accepting initializer_list
}
 类似资料:
  • 主要内容:类继承和初始化,初始化器继承和覆盖在Swift 4中声明的类,结构体和枚举初始化以准备类的实例。 存储属性初始化初始值,对于新实例也初始化值,初始化值以准备初始实例数据。 创建初始化由方法执行。 Swift 4初始化程序与Objective-C的不同之处在于它不返回任何值。 它是在处理之前检查新创建实例的初始化。 Swift 4还提供了“取消初始化”过程,用于在取消分配实例后执行内存管理操作。 存储属性的初始化程序角色 存储属性必

  • 多亏了C11,我们收到了系列的仿函数包装器。不幸的是,我一直只听到关于这些新添加的不好的消息。最受欢迎的是它们非常慢。我测试了它,与模板相比,它们真的很糟糕。 111毫秒对1241毫秒。我认为这是因为模板可以很好地内联,而通过虚拟调用覆盖内部。 显然,在我看来,模板也有其问题: 它们必须以头的形式提供,这不是您在以封闭代码形式发布库时可能不希望做的事情, 因此,我可以假设s可以用作传递函子的事实标

  • 考虑代码 输出是 据我所知,值被隐式转换为一个(第一个输出),然后初始化器构造函数开始工作(第二个输出)。我的问题是为什么会这样?标准构造函数不是更好的匹配吗?一、 例如,我本以为这个片段的输出就是ctor。 PS:如果我将构造函数标记为,那么是唯一调用的构造函数,因为整数现在不能隐式转换为。

  • 在下面的代码中,我展示了类似联合的类S,它包含两个不相关的结构B和C。我展示了如何实例化非POD std::字符串并再次删除它,然后将S切换到S::CC 并设置num int。 然而,我的目标是在std::initializer\u列表中使用S。我不知道初始化h的格式应该是什么。如果我想用这些S::BB,S::CC,S::BB初始化h,参数应该是什么? 我的编译器是VS2015。 编辑:这篇文章的

  • 我想这样使用std::ostream: 有一个错误,说ostream构造函数受到保护: 错误:'std::basic_ostream 但我记得

  • 在标准库中可以找到一个主要而明显的含义-“使用其元素列表初始化集合”: 在下面std::bitset上的链接后面可以找到另一个含义——“单个值是由initializer\u list的元素组合而成的”。 标准库中的第三个示例是std::piecewise\u constant\u distribution,但我不知道它有什么语义,而不是元素的集合。 std::initializer\u list构