Uniform Initialization
Uniform Initialization #
객체를 생성자와 함께 호출하려면 ( ) 를 사용했다.
1// vector(size_type count, const T& value) 호출해서 생성
2vector<int> vec(3, 10);
- Most Vexing Parse
아래의 경우에는 무슨 일이 일어날까?
기본생성자? vector를 리턴하고 인자를 받지않는 vec 함수?
c++에서는 이것을 함수의 선언으로 생각한다.
1vector<int> vec();
2class A {
3 public:
4 A() { std::cout << "A 의 생성자 호출!" << std::endl; }
5};
6class B {
7 public:
8 B(A a) { std::cout << "B 의 생성자 호출!" << std::endl; }
9};
10// c++ 컴파일러?는 애매한 표현을 허용해서 최적의 코드로 변환하는데,
11// 아래 코드는 A()가 익명타입 생성으로 B b(A anonymous)로 해석하는느낌
12B b(A());
함수 선언과 헷갈리는 문제를 해결하기 위해 { } 를 사용해서 모든 타입에 대해 일관된 초기화 방식을 제공한다.
생성자를 사용해서 순서대로 인자를 전달해주는 것이기 때문에 커스텀 클래스도 생성자만 구현해두면 된다.
단, 손실이 발생하는(Narrowing) 타입변환(=축소변환)을 지원하지 않아서 안전한 프로그래밍이 가능하다. (생성자는 int인데, float 데이터를 전달한 경우)
1std::list<int> my_list{1, 2, 3, 4, 5};
2std::set<int> my_set{3, 1, 5, 2, 4};
3
4class CustomClass {
5public:
6 std::string name;
7 int value;
8
9 CustomClass(const std::string& _name, int _value)
10 : name(_name), value(_value) {}
11};
12CustomClass obj1{"object1", 1};
13
14std::map<std::string, CustomClass> my_map{
15 {"one", {"kdh", 1}},
16 {"two", {"miny", 5}},
17 {"three", {"asap", 3}}
18};
initializer list 생성자 #
새로운 방식의 { }를 위한 생성자도 만들 수 있다.
생성자를 구현해두면, 다른 생성자보다 initializer list 생성자가 먼저 고려된다.
1#include <initializer_list>
2#include <iostream>
3#include <string>
4
5class A {
6 public:
7 A(int x, double y) { std::cout << "일반 생성자! " << std::endl; }
8
9 A(std::initializer_list<std::string> lst) {
10 std::cout << "string 초기화자 사용 생성자! " << std::endl;
11 }
12
13 /*A(std::initializer_list<int> lst) {
14 std::cout << "int 초기화자 사용 생성자! " << std::endl;
15 for (auto itr = lst.begin(); itr != lst.end(); ++itr) {
16 std::cout << *itr << std::endl;
17 }
18 }*/
19};
20
21A func(void) {
22 return { "string", "initializer" }; // 리턴도 대입이라 string 초기화생성자 호출
23}
24
25int main() {
26 A a(3, 1.5); // 일반
27 A b{"abc", "def"}; // string 초기화자
28
29 // 기본적으로 일반 초기화지만, 주석을 제거하면 int 초기화자 생성자가 호출된다.
30 // 그렇게되면 두번째 인자의 소숫점이 손실되어 Narrow Error 가 발생한다.
31 A c{3, 1.5};
32 A d = {3, 1.5};
33}
auto와 함께 쓰는 { } #
c++11 #
모든 경우 initializer_list
1auto a = {1}; // std::initializer_list<int>
2auto b{1}; // std::initializer_list<int>
3auto c = {1, 2}; // std::initializer_list<int>
4auto d{1, 2}; // std::initializer_list<int>
c++17~ #
auto x = {arg1, arg2, ...}형태의 경우 arg1, arg2 … 가 모두 같은 타입이라면initializer_list<T>로 추론된다.auto x{arg1, arg2, ...}형태의 경우 인자가 한개라면 인자의 타입으로 추론되고, 여러개라면 오류가 발생한다.
1auto a = {1}; // auto x = {} 형태 std::initializer_list<int>
2auto b = {1, 2}; // 위와 동일
3auto c{1}; // auto x{} 형태 이므로 타입추론해서 int
4auto d{1, 2}; // auto x{} 형태인데 인자가 2 개 이상이므로 컴파일 오류