经过一些调查后,看起来行为初始化变量遵循一些惯例。
对于单个元素:
auto* x = new int; // undefined value
auto* x = new int{}; // 0 (default initializer)
auto* x = new int(23); // 23 (copy initializer)
auto* x = new Test; // default constructor
auto* x = new Test{}; // default constructor
auto* x = new Test(...); // X constructor (overload-chosen)
这很有意义,直到你尝试在数组上应用相同的逻辑:
auto* x = new int[10]; // all undefined values - OK
auto* x = new int[10]{}; // all 0 (default initializer) - OK
auto* x = new int[10](23); // all 23 (copy initializer on all) - NOT IMPLEMENTED
auto* x = new Test[10]; // default constructors - OK
auto* x = new Test[10]{}; // default constructors - OK
auto* x = new Test[10](...); // X constructor on all (overload-chosen) - NOT IMPLEMENTED
我的逻辑说你可以做一些假设:
T(Args..)
构造类型,则可以构造数组中的每个元素。没有理由不允许T[N](Args...)
语法。有谁知道为什么这个功能不存在?允许
是非常好的new int[10](23); // all 23
new Test[10]("asd", 123); // construct all using Test("asd", 123)
编辑:这样做的全部思路是避免默认初始化/构造函数并直接调用我们需要的那个。
答案 0 :(得分:4)
如果在标准库 † 中有这样的东西会很好,那么你想要的功能相对简单易用:
namespace detail {
template<typename T, typename... ArgTs, std::size_t... Is>
constexpr auto make_filled_array(ArgTs const&... args, std::index_sequence<Is...>)
-> std::array<T, sizeof...(Is)> {
return {{(Is, T(args...))...}};
}
}
template<typename T, std::size_t N, typename... ArgTs, std::enable_if_t<N != 0, int> = 0>
constexpr auto make_filled_array(ArgTs const&... args) {
return detail::make_filled_array<T, ArgTs...>(args..., std::make_index_sequence<N>{});
}
// ...
auto arr1 = make_filled_array<int, 10>(23);
auto arr2 = make_filled_array<Test, 10>("asd", 123);
的 Online Demo 强>
那就是说,我认为在这里采用构造函数参数没有任何意义;容器emplace
函数很有用,因为它们完美转发,但我们不能在这里做,因为我们需要为每个构造函数调用重用参数(因此从它们移动不是一个选项)。我认为复制构造很自然:
namespace detail {
template<typename T, std::size_t... Is>
constexpr auto make_filled_array(T const& t, std::index_sequence<Is...>)
-> std::array<T, sizeof...(Is)> {
return {{(Is, t)...}};
}
}
template<std::size_t N, typename T, std::enable_if_t<N != 0, int> = 0>
constexpr auto make_filled_array(T const& t) {
return detail::make_filled_array(t, std::make_index_sequence<N>{});
}
// ...
auto arr1 = make_filled_array<10>(23);
auto arr2 = make_filled_array<10>(Test{"asd", 123});
的 Online Demo 强>
N.b。使用new
意味着您可能来自托管语言,需要阅读价值语义。 ; - ]此代码可以更改为返回std::array<T, N>*
或T*
(或者甚至是std::unique_ptr<T[]>
以获得适度合理的内容),但是,为什么会有一个... ?只需使用std::vector<T>
:
std::vector<int> vec(10, 23);
我选择在此处演示std::array<>
,因为您的示例都具有恒定的大小。
† 最让人想到的是std::fill
,但这不会初始化数组......
答案 1 :(得分:3)
我不能代表委员会发言,但我可以看到一个案例,你的语法非常有问题:从 temporaries:初始化。
X* x = new X[10](Y{});
你如何处理这个案子?
你不能为10个元素中的每个元素调用X(Y&&)
(假设它存在),因为你只有1个临时元素,而第一个元素之后的所有元素都会从未指定状态的对象初始化。
另一种选择是以某种方式调用X(const Y&)
(假设它再次存在)。除了使标准复杂化并添加意外的不一致行为,这只会打开另一种蠕虫。 X
对象可以对我们的Y{}
进行引用绑定,假设它是一个左值,并且最终会有一个悬空引用。
我看到唯一可行的解决方案是不允许对此语法进行右值引用,但这只会提供更多SO问题。
我认为标准委员会考虑到这一点是非常安全的。