swe*_*rup 50 c++ arrays initializer-list c++11
我正在快速使用C++ 0x,并使用g ++ 4.6进行测试
我只是尝试了下面的代码,认为它会工作,但它不会编译.我收到错误:
incompatible types in assignment of ‘std::initializer_list<const int>’ to ‘const int [2]’
struct Foo
{
int const data[2];
Foo(std::initializer_list<int const>& ini)
: data(ini)
{}
};
Foo f = {1,3};
Run Code Online (Sandbox Code Playgroud)
Joh*_*esD 55
您可以使用可变参数模板构造函数而不是初始化列表构造函数:
struct foo {
int x[2];
template <typename... T>
foo(T... ts) : x{ts...} { // note the use of brace-init-list
}
};
int main() {
foo f1(1,2); // OK
foo f2{1,2}; // Also OK
foo f3(42); // OK; x[1] zero-initialized
foo f4(1,2,3); // Error: too many initializers
foo f5(3.14); // Error: narrowing conversion not allowed
foo f6("foo"); // Error: no conversion from const char* to int
}
Run Code Online (Sandbox Code Playgroud)
编辑:如果你可以没有const,但另一种方法是跳过初始化并填充函数体中的数组:
struct foo {
int x[2]; // or std::array<int, 2> x;
foo(std::initializer_list<int> il) {
std::copy(il.begin(), il.end(), x);
// or std::copy(il.begin(), il.end(), x.begin());
// or x.fill(il.begin());
}
}
Run Code Online (Sandbox Code Playgroud)
但是,这样会丢失前一个解决方案提供的编译时边界检查.
Pot*_*ter 17
As far as I can tell, using list-initialization of the function argument of the constructor (8.5.4/1) should be legal and solves many of the issues of the above. However, GCC 4.5.1 on ideone.com fails to match the constructor and rejects it.
#include <array>
struct Foo
{
std::array< int, 2 > const data;
Foo(std::array<int, 2> const& ini) // parameter type specifies size = 2
: data( ini )
{}
};
Foo f( {1,3} ); // list-initialize function argument per 8.5.4/1
Run Code Online (Sandbox Code Playgroud)
If you really insist on initializer_list, you can use reinterpret_cast to turn the underlying array of the initializer_list into a C-style array.
Foo(std::initializer_list<int> ini) // pass without reference- or cv-qualification
: data( reinterpret_cast< std::array< int, 2 > const & >( * ini.begin() )
Run Code Online (Sandbox Code Playgroud)
根据这里的讨论:
Potatoswatter第二个解决方案的正确语法是:
Foo f( {{1,3}} ); //two braces
Run Code Online (Sandbox Code Playgroud)
有点难看,与常见用法不一致
如果没有参数传递给foo构造函数,则将默认初始化数组。但是有时您想保持底层数组未初始化(可能出于性能原因)。您不能将默认构造函数与可变参数模板化的一起添加。解决方法是可变参数模板构造函数的附加参数,以区别于零参数构造函数:
template<class T, size_t rows, size_t cols>
class array2d
{
std::array<T, rows * cols> m_Data;
public:
array2d() {}
template <typename T, typename... Types>
array2d(T t, Types... ts) : m_Data{ { t, ts... } } {}
};
Run Code Online (Sandbox Code Playgroud)
因此,现在您可以对对象进行括号初始化,也可以不对其进行初始化:
array2d<int, 6, 8> arr = { 0, 1, 2, 3 }; // contains 0, 1, 2, 3, 0, 0, 0, ...
array2d<int, 6, 8> arr2; // contains garbage
Run Code Online (Sandbox Code Playgroud)
更新31/07/2016
三年过去了,编译器实施者将其产品的标准合规性提高到了在存在可变参数构造函数的情况下默认构造函数不再被视为模棱两可的水平。因此,在实践中,我们不需要T t可变参数构造函数的附加参数来消除构造函数的歧义。
都
array2d() {}
Run Code Online (Sandbox Code Playgroud)
和
array2d() = default;
Run Code Online (Sandbox Code Playgroud)
如果构造的对象没有参数,将使数组未初始化。此行为在所有主要编译器上都是一致的。完整示例(rextester):
#include <array>
#include <iostream>
template<class T, size_t rows, size_t cols>
class array2d
{
public:
std::array<T, rows * cols> m_Data;
array2d() = default;
template <typename... Types>
array2d(Types... ts) : m_Data{ { ts... } } {}
};
int main()
{
array2d<int, 6, 8> arr_init = { 0, 1, 2, 3 };
array2d<int, 6, 8> arr_default;
std::cout << "Initialized: \n";
for(const auto& a : arr_init.m_Data)
std::cout << a << " ";
std::cout << "\n";
std::cout << "Default: \n";
for(const auto& a : arr_default.m_Data)
std::cout << a << " ";
std::cout << "\n";
}
Run Code Online (Sandbox Code Playgroud)
输出:
Initialized:
0 1 2 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
Default:
2 0 -519559849 32558 1 32558 0 0 -519634912 32558 -526739248 32558 1 0 2 0 6295032 0 -519531243 32558 0 0 -1716075168 32765 6295648 0 4196192 0 6295648 0 -526527271 32558 1 0 2 0 6295032 0 4196845 0 124 0 0 0 4196768 0 4196518 0
Run Code Online (Sandbox Code Playgroud)
删除默认构造函数仍然会导致可变参数构造函数被调用,并且数组将被默认初始化(在本例中为全零)。
感谢@Alek突破了这个线程并引起了对这些事实的关注,也感谢所有致力于编译器开发的人们。