C ++

比方说,我有一个存储类型T的对象的模板。 我想传递构造函数参数来初始化数据成员。 我应该使用统一初始化还是使用非花括号直接初始化?:

template<typename T>
struct X
{
    template<typename... Args>
    X(Args&&... args)
             : t(std::forward<Args>(args)...) // ?
    /* or */ : t{std::forward<Args>(args)...} // ?
private:
    T t;
};

如果我想存储的对象是一个std::vector并且选择了花括号样式(统一初始化),那么我传递的参数将被转发给vector::vector(std::initializer_list<T>)构造方法,这可能是也可能不是我想要的。

另一方面,如果我使用非花括号样式,则无法通过其std::initializer_list构造函数向vector添加元素。

当我不知道我存储的对象和将要传入的参数时,应该使用什么样的初始化形式?


为了清楚起见,对于具有多个构造函数的类型(包括采用std::initializer_list )和另一个参数(当用花括号初始化时)可能被编译器解释为std::initializer_list的类型出现歧义。 例如, std::vector<int>就是这种情况:

template<typename T>
struct X1
{
    template<typename... Args>
    X1(Args&&... args)
             : t(std::forward<Args>(args)...) {}

    T t;
};

template<typename T>
struct X2
{
    template<typename... Args>
    X2(Args&&... args)
     : t{std::forward<Args>(args)...} {}

    T t;
};

int main() {
    auto x1 = X1<std::vector<int>> { 42, 2 };
    auto x2 = X2<std::vector<int>> { 42, 2 };

    std::cout << "size of X1.t : " << x1.t.size()
              << "nsize of X2.t : " << x2.t.size();
}

(请注意,唯一的区别是在X2成员初始值设定项列表中的大括号而不是X1成员初始值设定项列表中的括号)

输出

大小为X1.t:42

大小为X2.t:2

演示


标准库作者在编写实用程序模板(如std::make_uniquestd::make_sharedstd::optional<> (应该完全转发任何类型))时面临这个实际问题:哪种初始化形式是首选? 这取决于客户端代码。

没有很好的答案,他们通常带括号(理想情况下记录选择,因此呼叫者知道期望什么)。 习惯上现代的c + + 11是喜欢无处不在的初始化初始化(它避免了缩小转换,避免c ++最棘手的解析,等等。)


消除歧义的潜在解决方法是使用命名标签,在Andrzej的C ++博客的这篇伟大文章中进行了广泛讨论:

namespace std{
  constexpr struct with_size_t{} with_size{};
  constexpr struct with_value_t{} with_value{};
  constexpr struct with_capacity_t{} with_capacity{};
}

// These contructors do not exist.
std::vector<int> v1(std::with_size, 10, std::with_value, 6);
std::vector<int> v2{std::with_size, 10, std::with_value, 6};

这是详细的,只适用于你可以修改不明确的类型(例如,暴露了构造函数的类型(使用std::initializer_list和其参数列表可能转换为std::initializer list其他构造std::initializer list


与任何初始化一样,

  • 当对象包含一个值或多个分段初始化的值时使用大括号。 这包括聚合类和数字。

    大括号看起来更像是一个项目清单。

  • 当参数计算对象的初始状态时使用括号。

    圆括号更像是一系列函数参数。

  • 这个通用规则包括像std::vector<int>这样的容器,它可以用一个数字的N个副本( std::vector<int>(4,5) )或一对数字( std::vector<int>{4,5} )。

    顺便说一下,由于“统一”初始化并不是一蹴而就的,所以这个术语是不鼓励的。 官方名称是大括号初始化。

    链接地址: http://www.djcxy.com/p/83015.html

    上一篇: c++

    下一篇: Explaining IIFE Inside Loop