developer tip

emplace_back ()이 균일 한 초기화를 사용하지 않는 이유는 무엇입니까?

optionbox 2020. 12. 2. 08:31
반응형

emplace_back ()이 균일 한 초기화를 사용하지 않는 이유는 무엇입니까?


다음 코드 :

#include <vector>

struct S
{
    int x, y;
};

int main()
{
    std::vector<S> v;
    v.emplace_back(0, 0);
}

GCC로 컴파일하면 다음 오류가 발생합니다.

In file included from c++/4.7.0/i686-pc-linux-gnu/bits/c++allocator.h:34:0,
                 from c++/4.7.0/bits/allocator.h:48,
                 from c++/4.7.0/vector:62,
                 from test.cpp:1:
c++/4.7.0/ext/new_allocator.h: In instantiation of 'void __gnu_cxx::new_allocator<_Tp>::construct(_Up*, _Args&& ...) [with _Up = S; _Args = {int, int}; _Tp = S]':
c++/4.7.0/bits/alloc_traits.h:265:4:   required from 'static typename std::enable_if<std::allocator_traits<_Alloc>::__construct_helper<_Tp, _Args>::value, void>::type std::allocator_traits<_Alloc>::_S_construct(_Alloc&, _Tp*, _Args&& ...) [with _Tp = S; _Args = {int, int}; _Alloc = std::allocator<S>; typename std::enable_if<std::allocator_traits<_Alloc>::__construct_helper<_Tp, _Args>::value, void>::type = void]'
c++/4.7.0/bits/alloc_traits.h:402:4:   required from 'static void std::allocator_traits<_Alloc>::construct(_Alloc&, _Tp*, _Args&& ...) [with _Tp = S; _Args = {int, int}; _Alloc = std::allocator<S>]'
c++/4.7.0/bits/vector.tcc:97:6:   required from 'void std::vector<_Tp, _Alloc>::emplace_back(_Args&& ...) [with _Args = {int, int}; _Tp = S; _Alloc = std::allocator<S>]'
test.cpp:11:24:   required from here
c++/4.7.0/ext/new_allocator.h:110:4: error: new initializer expression list treated as compound expression [-fpermissive]
c++/4.7.0/ext/new_allocator.h:110:4: error: no matching function for call to 'S::S(int)'
c++/4.7.0/ext/new_allocator.h:110:4: note: candidates are:
test.cpp:3:8: note: S::S()
test.cpp:3:8: note:   candidate expects 0 arguments, 1 provided
test.cpp:3:8: note: constexpr S::S(const S&)
test.cpp:3:8: note:   no known conversion for argument 1 from 'int' to 'const S&'
test.cpp:3:8: note: constexpr S::S(S&&)
test.cpp:3:8: note:   no known conversion for argument 1 from 'int' to 'S&&'

그 제안은 vector정규 사용이다 ()의 인수의 요소를 구성하는 생성자 구문 emplace_back(). 위와 같은 예제를 만들기 위해 대신 균일 초기화 구문을 vector사용 하지 않는 이유는 무엇 {}입니까?

사용하여 잃을 게 없다는 것을 나에게 보인다 {}(이 하나있을 때 생성자를 호출하지만 일이 없을 때 여전히 작동)를, 그리고 그것을 사용하는 11 ++ C의 정신에 더 많은 것 {}- 후 모두, 균일 한 초기화의 요점은 객체를 초기화하기 위해 균일하게, 즉 모든 곳에서 사용된다는 것입니다.


위대한 마음은 비슷하게 생각합니다; v). 나는 결함 보고서를 제출하고 바로이 주제에 대한 표준 변경을 제안했습니다.

http://cplusplus.github.com/LWG/lwg-active.html#2089

또한 Luc Danton은 std :: allocator에서 직접 대 균일 초기화 의 어려움을 이해하는 데 도움이되었습니다 .

EmplaceConstructible (23.2.1 [container.requirements.general] / 13) 요구 사항이 개체를 초기화하는 데 사용되면 직접 초기화가 발생합니다. 집계를 초기화하거나 emplace와 함께 std :: initializer_list 생성자를 사용하려면 초기화 된 유형의 이름을 지정하고 임시로 이동해야합니다. 이것은 목록 초기화 ( "균일 초기화"라고도 함) 구문이 아닌 직접 초기화를 사용하는 std :: allocator :: construct의 결과입니다.

std :: allocator :: construct를 변경하여 list-initialization을 사용하면 무엇보다도 std :: initializer_list 생성자 오버로드를 선호하여 직관적이지 않고 수정할 수없는 방식으로 유효한 코드를 깨뜨릴 수 있습니다. emplace_back이 생성자에 액세스 할 수있는 방법이 없습니다. 기본적으로 push_back을 다시 구현하지 않고 std :: initializer_list에 의해 선점됩니다.

std::vector<std::vector<int>> v;
v.emplace_back(3, 4); // v[0] == {4, 4, 4}, not {3, 4} as in list-initialization

제안 된 절충안은 std :: is_constructible과 함께 SFINAE를 사용하는 것입니다. 이는 직접 초기화가 제대로 구성되었는지 테스트합니다. is_constructible이 false이면 목록 초기화를 사용하는 대체 std :: allocator :: construct 오버로드가 선택됩니다. 목록 초기화는 항상 직접 초기화로 대체되므로 직접 초기화 오버로드가 실패 할 수 없기 때문에 사용자는 목록 초기화 (균일 초기화)가 항상 사용중인 것처럼 진단 메시지를 보게됩니다.

I can see two corner cases that expose gaps in this scheme. One occurs when arguments intended for std::initializer_list satisfy a constructor, such as trying to emplace-insert a value of {3, 4} in the above example. The workaround is to explicitly specify the std::initializer_list type, as in v.emplace_back(std::initializer_list(3, 4)). Since this matches the semantics as if std::initializer_list were deduced, there seems to be no real problem here.

The other case is when arguments intended for aggregate initialization satisfy a constructor. Since aggregates cannot have user-defined constructors, this requires that the first nonstatic data member of the aggregate be implicitly convertible from the aggregate type, and that the initializer list have one element. The workaround is to supply an initializer for the second member. It remains impossible to in-place construct an aggregate with only one nonstatic data member by conversion from a type convertible to the aggregate's own type. This seems like an acceptably small hole.

참고URL : https://stackoverflow.com/questions/8782895/why-doesnt-emplace-back-use-uniform-initialization

반응형