최근 포토로그


C++11 auto와 {}-init-list 의 모호함 복잡한컴퓨터이야기

C++11에는 auto라는 키워드를 통해서 타입추론(deduction)을 수행하도록 기능이 추가 되었습니다. 이제 auto를 사용하면 할당 연산자나 객체 초기화 시에 객체의 타입을 명시적으로 지정하지 않아도 되기 때문에 코드를 간결하게 쓸 수 있습니다. 몇 가지 예를 살펴보시죠.

auto v1 = 10;

auto v2 = ‘c’;

auto v3 { f() };

for (auto &each : v)

           //do something

이것이 가능한 이유는 rValue로부터 타입을 추론할 수 있기 때문에 굳이 객체의 타입을 명시할 필요가 없기 때문이지요. 즉 위의 첫 번째 예를 살펴보면 할당 연산자 우측의 10이 이미 int 타입이기 때문에 v1을 명시적으로 int로 선언하지 않아도 컴파일러가 v1int로 추론할 수 있기 때문입니다.

또한 C++11에서는 {}를이용한 객체 initialize가 가능합니다. 이를 이용하면 다음과 같이 객체를 초기화 할 수 있기 때문에 위 예제 중 2가지를 고쳐 쓸 수 있습니다.

auto v1 {10};

auto v2{ ‘c’ };

 

또한 vector와 같은 container들에 객체를 삽입할 경우에 반복적으로 push_back()을 하는 수고를 덜기 위해서 {}-init-list 라는 기능을 제공하는데요. 간단히 예를 통해서 알아보면

vector<int> v = {1, 2, 3};

list<int> l = {1, 2, 3};

라고 작성할 수 있지요. 이 경우 할당 연산의 우측 { }는 컴파일시에 std::initializer_list<T> 타입으로 해석되며, vector liststd::initializer_list<T> 타입을 매개변수로 취하는 생성자가 호출되게 됩니다.

,

auto v1 = {1,2,3};

이라고 할 경우 v1std::initializer_list<int>타입으로 타입 추론이 수행됩니다. vector<int>list<int>를 사용한 예제는 다음과 같이 {}-init-list를이용하여 다음과 같이 작성할 수도 있습니다.

vector<int> v {1, 2, 3};

list<int> l {1, 2, 3};

만약, 원소가 1개인 vector list를 작성하려면 다음과 같이 작성할 수도 있겠지요.

vector<int> v{1};

list<int> l{1};

 

여기까지 이해가 되셨다면 다음과 같은 예제를 살펴보겠습니다.

 

int v1{ 123 };                 <- 1

auto v2{ 123 };              <- 2

auto v3 = { 123 };          <- 3

auto v4{ 1, 2, 3 };           <- 4

auto v5 = { 1, 2, 3 };       <- 5

 

1은 명확합니다. v1 123 이라는 정수 값을 가지도록 초기화 됩니다.

2는 어떨까요? 약간 애매합니다. 왜냐하면 v2 123이라는한 개의 정수를 가진 initializer_list 컬렉션으로 해석해야 할지 아니면 123이라는 정수 값을 가진 int v2로 해석해야 할지 혼돈스럽습니다.

3의 경우는 어떠할까요? 이또한 명확합니다. v3initializer_list<int>로추론됩니다.

4의 경우는 3의 경우와 마찬가지로 initializer_list<int>로 추론되는 것이 맞을 것 같습니다.

5의 경우도 비교적 명확해 보입니다.이 또한 initializer_list<int>로 추론 될 것입니다.

 

여기서 주의 깊게 살펴 보아야할 것은 2번과 4번입니다.

먼저 C++ 11의 경우라면 2initializer_list<int>로 추론이 이루어지며, 4번 또한 동일합니다. 의미론적으로는 명확해 보입니다. 하지만 개발자의 입장에서는 이것이 보통 혼돈스러운 것이 아닙니다. 아래를 보시죠.

int v1{ 123 };                 <- 1

auto v2{ 123 };              <- 2

auto v5 = { 1, 2, 3 };       <- 5

단지 int auto로 변경했을 뿐인데, 1의 경우는 v1int로 추론하고 123을 할당하였습니다. 하지만 25의 경우는 v2v5initializer_list<int>로해석하여 일종의 collection으로 초기화 합니다.

1 2만 살펴보면, 이 둘은 그 구조가 매우 유사함에도 명시적으로 타입을 지정하느냐 하지 않느냐에 따라 혼돈스럽게 동작합니다. 게다가 다음과 같은 MyType이 정의되어 있다고 가정해 봅시다.

class MyType {

public:

           int_x, int _y;

           MyType(intx, int y) : _x{ x }, _y{ y } {}

};

 

이 경우 MyType 객체를 생성하기 위해서 다음과 같이 코드를 작성할수 있습니다.

MyType v3{1, 2};

MyType 2개의 정수값을 취하는 생성자를 가지고 있기 때문에 위 코드는 MyType 형 객체를 생성하고 _x, _y를 각각 1,2로 초기화 합니다. 이제는 더욱 혼란스럽습니다.

이러한 혼란을 정리하고자 Bjarne Stroustrup auto{}-init-list를 동시에 사용할 경우에는 모호함을피하기 위해서 반드시 할당 연산을 이용하라고 말하고 있습니다.

auto v2 = {123};

auto v5 = {1, 2, 3};

으로만 사용할 것을 권고 한다는 것이죠.

여러분은 어떻게 생각하실지 모르지만, 마이크로소프트의 일부 개발자들은 이러한 문법적 모호성을 개선할 필요가 있다고 느낀 것 같습니다. 이에 지난 6C++ WG21standard meeting을 통해서 N3922(http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2014/n3922.html)를 제안하였으며, 11월에 있을 다음 standard meeting에서채택 여부를 결정하기로 하였다고 합니다. (이렇게 결정된 내용은 아마도 C++14에 반영될 것 같습니다.) N3922의 내용을 간단히 살펴보면, auto에 직접 붙여서 {}-init-list를 사용하는 경우는element를 하나만 쓸 수 있게 한다. 동시에 이 경우는 list가 아니라 객체 초기화로 간주한다. 입니다. 이게 무슨 말인고 하니,

auto v2{ 123 };              <- 2

와 같은 문장을 기존에는 123이라는 한 개의 요소를 가진 initializer_list<int>로 추론하였으나 이제는 그렇게 하지 않고 정수 타입으로 추론한다는것입니다. 그리고

auto v4{ 1, 2, 3 };           <- 4

와 같은 문장은 허용하지 않을 것이며, 반드시 이렇게 초기화를 해야한다면

auto v4 = {1, 2, 3};

과 같이 초기화 해야 한다는 것이지요.

제가 알고 있는 한도 내에서는 N3922가 반영되어 있는 유일한 컴파일러는Visual Studio 14 CTP3/4 이며 VisualStudio 2013 Update 3GCCN3922를반영하지 않았습니다. (당연합니다. 아직 표준으로 받아들여지지 않았으니까요.)

여러분은 어떤 것이 더 직관적이고 올바른 문법 규정이라고 생각하시나요? 저는개인적으로 N3922가 반드시 받아들여 졌으면 좋겠습니다.

 


덧글

댓글 입력 영역


facebook 프로필 위젯

트위터 위젯