c – 初始化时的统一或直接初始化?

前端之家收集整理的这篇文章主要介绍了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并且我选择了花括号样式(uniform-initialization),那么我传递的参数将被转发到vector :: vector(std :: initializer_list< T>)构造函数,这可能是也可能不是我想要的.

另一方面,如果我使用非大括号样式,我将失去通过其std :: initializer_list构造函数向向量添加元素的能力.

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

解决方法

为了清楚起见,对于具有多个构造函数的类型产生了歧义,包括一个使用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成员初始化列表中的括号)

Output :

size of X1.t : 42

size of X2.t : 2

Demo

标准库作者在编写实用程序模板时遇到了这个问题,例如std :: make_unique,std :: make_shared或std :: optional<> (应该完全转发任何类型):首选哪种初始化形式?这取决于客户端代码.

没有好的答案,他们通常使用括号(理想情况下记录选择,因此调用者知道会发生什么).习惯性的现代c 11更喜欢在任何地方进行支撑初始化(它避免缩小转换,避免c最令人烦恼的解析等).

消除歧义的潜在解决方法是使用命名标记,在Andrzej’s C++ blog这篇伟大的文章中广泛讨论:

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,6};

这是详细的,并且只有在您可以修改歧义类型时才适用(例如,暴露构造函数的类型采用std :: initializer_list和其参数列表可能转换为std :: initializer列表的其他构造函数)

猜你在找的C&C++相关文章