Uniform Initialization
Uniform Initialization
유니폼 초기화 혹은 균일 초기화라고 부른다.
생기게된 계기
C++ 에서 변수를 초기화 할 때, 아래와 같은 실수를 저지른다.
int a();
MyClass b();
이 때, a와 b는 작동 방식이 다르다. a는 int자료형을 Direct Initialization한 것이고, b는 MyClass라고 하는 자료형을 반환하는 함수를 정의한 것 이기 때문에, 생성자가 호줄되지 않는다. (전방선언)
함수의 정의처럼 보이는 것들은 모두 함수의 정의로 해석
다른 예로는,
B b(A());
의 경우, 인자값을 받지 않고 A를 return 하는 함수를, 다시 인자값으로 받아 B를 return하는 함수 b인 것이다. (우리는 class A의 기본 생성자로 생성된 객체 A를 인자값으로 받는 B 생성자로 b를 생성했다고 생각했지만)
문제는, 소괄호 ()가 함수를 인자를 정의할 때도, 객체의 생성자를 호출할 때도 사용되기 때문
그래서 이런 문제를 해결하고자 나온 것이, {}를 사용하는 Uniform Initialization이다.
Uniform Initialization 의 특징
()생성과 {}생성의 차이점
데이터 손실이 있는 형 변환 (Narro Conversion) 의 가능 여부
int A(3.5f) // Narrow-Conversion 가능
int B{3.f} // Narro-Conversion 불가 -> Error 발생
반환 할 때의 특징
Uniform Initialization 으로 반환할 때에는, 생성할 객체의 타입을 명시하지 않아도 된다는 특징이 있다.
class A
{
public:
A(int x, double y) {}
}
A func()
{
return {3, 3.5}; // A(3, 3.5) 와 동일
}
이 경우 컴파일러가 함수의 반환값을 보고 추론하여 A의 생성자를 호출한다.
Initializer_list (초기화자 리스트)
배열을 초기화할 때, {}를 사용할 수 있다.
int arr[] = {1, 2, 3, 4};
Uniform Initialization을 사용하면, 객체도 이처럼 초기화할 수 있는데, 이 때 Initializer_list 가 사용된다.
class A
{
public:
A(std::initializer_list<int> l)
{
for (auto iter = l.begin(), iter != l.end(); ++iter)
{
// Todo : Use *iter
}
}
}
int main()
{
A a = {1, 2, 3, 4};
}
Initializer_list 사용시 주의할 점
- ()을 사용해 생성하면 initializer_list 를 사용할 수 없다.
- 원소의 갯수가 1개일 때 주의할점
- 벡터 생성 시
vector<int> a(10); // 원소 10개짜리 벡터 생성 vector<int> b{10}; // 10을 원소로 가지는 1개짜리 벡터 생성 // vector<int> b = {10} 와 동일;
- 벡터 생성 시
- initializer_list 가 최우선으로 고려되어 생기는 문제점
class A { public: A(int x, double y) { std::cout << "일반 생성자! " << std::endl; } A(std::initializer_list<int> lst) { std::cout << "초기화자 사용 생성자! " << std::endl; } }; int main() { A a(3, 1.5); // Good A b{3, 1.5}; // Bad! -> initializer_list<int> 인데, 1.5인 double 이 들어왔음 -> Narrow-Conversion이 안되므로 error 발생 }
- auto를 사용하여 생기는 문제점
- 원소의 갯수에 따른 문제(해결됨)
auto a{1}; //std::initializer_list<int> auto b{2, 3, 4}; //std::initializer_list<int> // 즉 둘 다 배열로 간주된다는 점
- 하지만 이는 원소의 갯수가 1개이나, 2개 이상이냐로 나뉘어 해결됨
auto a{1}; // 그냥 int auto b{2, 3, 4}; //std::initializer_list<int>
- 하지만 이는 원소의 갯수가 1개이나, 2개 이상이냐로 나뉘어 해결됨
- 문자열에 대한 문제
auto list = {"abc", "def", "ghi"}; //std::initilizer_list<string>이 아닌 std::initilizer_list<const char*>이 됨 // 이를 해결하기 위해서는 C++14에서 추가된 리터럴 연산자를 사용 using namespace std::literals; auto stringList = {"abc"s, "def"s, "ghi"s}; // 이러면 std::initilaizer_list<string>으로 처리됨
- 원소의 갯수에 따른 문제(해결됨)