C++11中初始化列表initializer lists的使用方法

C++11引入了初始化列表来初始化变量和对象。自定义类型,如果想用初始化列表就要包含initializer_list头文件。

C++11将使用大括号的初始化(列表初始化)作为一种通用初始化方式,可用于所有类型。初始化列表不会进行隐式转换。

C++11提供的新类型,定义在头文件中。

template< class T >
class initializer_list;

先说它的用处吧,然后再详细介绍一下。

首先有了initializer_list之后,对于STL的container的初始化就方便多了,比如以前初始化一个vector需要这样:

int a[] = {0, 1, 2, 3};
std::vector vec(a, a+sizeof(a));

或者

std::vector vec;
vec.push_back(1);
vec.push_back(3);
vec.push_back(3);
vec.push_back(2);

有了initializer_list后,就可以直接像初始化数组一样:

class Test {

private:
    static std::map const nameToBirthday = {
        {"lisi", "18841011"},
        {"zhangsan", "18850123"},
        {"wangwu", "18870908"},
        {"zhaoliu", "18810316"},
    };
}

当然啦,里面的std::map必须提供参数为initializer_list的构造函数如:

map( std::initializer_list init,
const Compare& comp = Compare(),
const Allocator& alloc = Allocator() );

其实for(initializer: list)中如果list是个形如:{a, b, c...},那么其实list自动被构造成了initializer_list对象。

下面稍微介绍一下initializer_list

一个initializer_list当出现在以下两种情况的被自动构造:

当初始化的时候使用的是大括号初始化,被自动构造。包括函数调用时和赋值
当涉及到for(initializer: list),list被自动构造成initializer_list对象
也就是说initializer_list对象只能用大括号{}初始化。

拷贝一个initializer_list对象并不会拷贝里面的元素。其实只是引用而已。而且里面的元素全部都是const的。

下面一个例子可以帮助我们更好的理解如何使用initializer_list:

#include 
#include 
#include 

using namespace std;

template 
struct S {
    vector v;
    S(initializer_list l) : v(l){
        cout << "constructed with a " << l.size() << "-elements lists" << endl;
    }
    void append(std::initializer_list l) {
        v.insert(v.end(), l.begin(), l.end());
    }

    pair c_arr() const{
        return {&v[0], v.size()};
    }

};


template 
void templated_fn(T arg) {
    for (auto a : arg)
        cout << a << " ";
    cout << endl;
}

int main() {
    S s = {1, 2, 3, 4, 5}; //automatically construct a initializer_list 
                                // object and copy it
    s.append({6, 7 , 8});         //list-initialization in function call

    cout << "The vector size is now " << s.c_arr().second << " ints:" << endl;

    for (auto n : s.v)
        cout << ' ' << n;
    cout << endl;

    cout << "range-for over brace-init-list: " << endl;
    
    for (auto x : {-1, -2, 03})   //// the rule for auto makes this ranged for work
        cout << x << " ";
    cout << endl;

    auto al = {10, 11, 12};  //special rule for auto

    cout << "The list bound to auto has size() = " << al.size() << endl;


    //templated_fn({1, 2, 3});   //compiler error! "{1, 2, 3}" is not an expressionit has no type, and so T cannot be duduced.

    templated_fn > ({7, 8, 9}); //ok
    templated_fn >({3, 5, 7});           //also ok

    return 0;
}

下面是从其他文章中copy的测试代码,详细内容介绍可以参考对应的reference:

#include "init_list.hpp"
#include 
#include 
#include 
#include 
#include 
 
///
// reference: http://en.cppreference.com/w/cpp/language/list_initialization
struct Foo {
	//std::vector mem = { 1, 2, 3 }; // list-initialization of a non-static member
	//std::vector mem2;
	//Foo() : mem2{ -1, -2, -3 } {} // list-initialization of a member in constructor
};
 
std::pair f(std::pair p)
{
	return{ p.second, p.first }; // list-initialization in return statement
}
 
int test_init_list1()
{
	int n0{};     // value-initialization (to zero)
	int n1{ 1 };    // direct-list-initialization
	std::string s1{ 'a', 'b', 'c', 'd' }; // initializer-list constructor call
	std::string s2{ s1, 2, 2 };           // regular constructor call
	std::string s3{ 0x61, 'a' }; // initializer-list ctor is preferred to (int, char)
 
	int n2 = { 1 }; // copy-list-initialization
	double d = double{ 1.2 }; // list-initialization of a temporary, then copy-init
 
	std::map m = { // nested list-initialization
			{ 1, "a" },
			{ 2, { 'a', 'b', 'c' } },
			{ 3, s1 }
	};
 
	std::cout << f({ "hello", "world" }).first << '\n'; // list-initialization in function call
 
	const int(&ar)[2] = { 1, 2 }; // binds a lvalue reference to a temporary array
	int&& r1 = { 1 }; // binds a rvalue reference to a temporary int
	//  int& r2 = {2}; // error: cannot bind rvalue to a non-const lvalue ref
 
	//  int bad{1.0}; // error: narrowing conversion
	unsigned char uc1{ 10 }; // okay
	//  unsigned char uc2{-1}; // error: narrowing conversion
 
	Foo f;
 
	std::cout << n0 << ' ' << n1 << ' ' << n2 << '\n'
		<< s1 << ' ' << s2 << ' ' << s3 << '\n';
	for (auto p : m)
		std::cout << p.first << ' ' << p.second << '\n';
	//for (auto n : f.mem)
	//	std::cout << n << ' ';
	//for (auto n : f.mem2)
	//	std::cout << n << ' ';
 
	return 0;
}
 
 
// reference: https://mbevin.wordpress.com/2012/11/16/uniform-initialization/
int test_init_list2()
{
	int arr[]          { 1, 2, 3, 4, 5 };
	std::vector v{ 1, 2, 3, 4, 5 };
	std::set s{ 1, 2, 3, 4, 5 };
	std::map m{ { 0, "zero" }, { 1, "one" }, { 2, "two" } };
 
	return 0;
}
 
///
// reference: https://mbevin.wordpress.com/2012/11/16/uniform-initialization/
// 'aggregate' class - no user-declared constructor, no private/protected members, no base, no virtual function
struct ClassA {
	int x;
	double y;
};
 
// non-aggregate class
class ClassB {
private:
	int x;
	double y;
public:
	ClassB(int _x, double _y) :x(_x), y(_y) {}
};
 
std::pair multiplyVectors(
	std::pair v1,
	std::pair v2) {
	return{ v1.first*v2.first, v1.second*v2.second };
}
 
int test_init_list3()
{
	int i{ 3 };
	int j{}; // empty braces initialize the object to it's default (0)
	std::string s{ "hello" };
 
	ClassA objA1{};
	ClassA objA2{ 1, 2.0 };
	ClassB objB1{ 1, 2.0 };
	ClassA arrOfAs[] = { { 1, 1.0 }, { 2, 2.0 }, { 3, 3.0 } };
 
	// ouch, the theory is that this should work in C++11, however this doesn't compile, at least with clang, comments?
	ClassB arrOfBs[] = { { 1, 1.0 }, { 2, 2.0 }, { 3, 3.0 } };
 
	// however, this does work
	std::vector vectorOfBs = { { 1, 1.0 }, { 2, 2.0 }, { 3, 3.0 } };
 
	auto result = multiplyVectors({ 1.0, 2.0 }, { 3.0, 4.0 });
 
	return 0;
}

GitHub: https://github.com/fengbingchun/Messy_Test

接着看一下Blackops同学的补充

由于最近数据结构有个实验报告说是要对字符串进行排序,想偷个懒不想一个一个地赋值,虽然可以用strcpy和传入二级指针的形式直接写,但是这样感觉不美观漂亮。

  然后就去膜了一下C++11的新特性——初始化列表,概念就不说了,就讲下这东西具体怎么用吧,就是正常的写一个构造函数,然后把参数改为initializer_list<数据类型> &t如图所示

C++11中初始化列表initializer lists的使用方法_第1张图片

可以理解为传入的参数数据被放到了一个储存器t中,利用C++11的auto可以直接遍历这个储存器t,然后把遍历到的值给结构体用。这里用的是char 数组而不是int是因为这里有一个问题,如果把initializer_list尖括号里的类型改为char *,则会报错,因为普通的""双引号代表的字符串为实际为const 类型,实际本身为一个指针,指向全局const变量中的地址,因此const char *a="123", *b="123",打印a与b的地址会发现a与b是相同的,早上就是因为这个地方百度了一会儿才明白,另外一个点就是若声明的是info数组,那么显然要用info的实例对象去初始化,因此还要把字符串加上大括号来形成一个个info实例对象

下面是完整代码:

#include 
#include 
using namespace std;
#define INF 0x3f3f3f3f
#define LC(x) (x<<1)
#define RC(x) ((x<<1)+1)
#define MID(x,y) ((x+y)>>1)
#define CLR(arr,val) memset(arr,val,sizeof(arr))
#define FAST_IO ios::sync_with_stdio(false);cin.tie(0);
typedef pair pii;
typedef long long LL;
const double PI = acos(-1.0);
struct info
{
    char name[20];
    info() {}
    info(const initializer_list t)
    {
        for (auto &item : t)
            strcpy(name, item);
    }
};
 
info one[2] = {{"this is one"}, {"this is two"}};
int main(void)
{
    for (auto &it : one)
        cout << it.name << endl;
    return 0;
}

上面是直接用const指针进行初始化,那如果我已经有了一个字符串的二维数组像这样:char s[maxn][maxn] = {"1", "2", "3", "4"},那如何赋值呢?显然把每一个字符串的首地址的地址传进去即可。

#include 
#include 
using namespace std;
#define INF 0x3f3f3f3f
#define LC(x) (x<<1)
#define RC(x) ((x<<1)+1)
#define MID(x,y) ((x+y)>>1)
#define CLR(arr,val) memset(arr,val,sizeof(arr))
#define FAST_IO ios::sync_with_stdio(false);cin.tie(0);
typedef pair pii;
typedef long long LL;
const double PI = acos(-1.0);
const int N = 20;
char s[N][N] = {"this is one", "this is two", "3", "4"};
struct info
{
    char name[20];
    info() {}
    info(const initializer_list t)
    {
        for (auto &item : t)
            strcpy(name, item);
    }
};
 
info one[2] = {{s[0]}, {s[1]}};
int main(void)
{
    for (auto &it : one)
        cout << it.name << endl;
    return 0;
}

似乎更多的情况并不是什么用字符串赋值,而是像int、double或者自定义结构体这样的赋值,那如何从初始化列表中一个一个读取并连续地赋值到一维或更高维的数组里呢?这里用到了C++11的另一个特性,auto 类型,这样就可以方便地进入初始化列表中去

以HDU 1005为例,如果这题用矩阵快速幂做,那么可以参考以下代码:

#include 
#include 
using namespace std;
const int N = 2;
const int MOD = 7;
const int F[3] = {0, 1, 1};
struct Mat
{
    int A[N][N];
    Mat()
    {
        for (int i = 0; i < N; ++i)
            for (int j = 0; j < N; ++j)
                A[i][j] = 0;
    }
    Mat(initializer_list rhs)
    {
        auto it = rhs.begin();
        for (int i = 0; it != rhs.end(); ++it, ++i)
            A[i >> 1][i & 1] = *it;
    }
    Mat operator*(const Mat &rhs)
    {
        Mat c;
        for (int i = 0; i < N; ++i)
            for (int j = 0; j < N; ++j)
                for (int k = 0; k < N; ++k)
                    c.A[i][j] = (c.A[i][j] + A[i][k] * rhs.A[k][j]) % MOD;
        return c;
    }
    friend Mat operator^(Mat a, int b)
    {
        Mat r;
        for (int i = 0; i < N; ++i)
            r.A[i][i] = 1;
        while (b)
        {
            if (b & 1)
                r = r * a;
            a = a * a;
            b >>= 1;
        }
        return r;
    }
};
int main(void)
{
    int A, B, n;
    while (~scanf("%d%d%d", &A, &B, &n) && (A || B || n))
    {
        Mat left =
        {
            A, B,
            1, 0
        };
        Mat right =
        {
            F[2], 0,
            F[1], 0
        };
        if (n <= 2)
            printf("%d\n", F[n]);
        else
        {
            left = left ^ (n - 2);
            Mat resultMat = left * right;
            printf("%d\n", resultMat.A[0][0]);
        }
    }
    return 0;
}

到此这篇关于C++11中初始化列表initializer lists的使用方法的文章就介绍到这了,更多相关initializer lists初始化列表内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

你可能感兴趣的:(C++11中初始化列表initializer lists的使用方法)