Uniform Initialization

Uniform Initialization

2023년 7월 4일
c++11, c++17

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 개 이상이므로 컴파일 오류
comments powered by Disqus