[c++] C ++ 11 emplace_back on vector <struct>?

다음 프로그램을 고려하십시오.

#include <string>
#include <vector>

using namespace std;

struct T
{
    int a;
    double b;
    string c;
};

vector<T> V;

int main()
{
    V.emplace_back(42, 3.14, "foo");
}

작동하지 않습니다.

$ g++ -std=gnu++11 ./test.cpp
In file included from /usr/include/c++/4.7/x86_64-linux-gnu/bits/c++allocator.h:34:0,
                 from /usr/include/c++/4.7/bits/allocator.h:48,
                 from /usr/include/c++/4.7/string:43,
                 from ./test.cpp:1:
/usr/include/c++/4.7/ext/new_allocator.h: In instantiation of ‘void __gnu_cxx::new_allocator<_Tp>::construct(_Up*, _Args&& ...) [with _Up = T; _Args = {int, double, const char (&)[4]}; _Tp = T]’:
/usr/include/c++/4.7/bits/alloc_traits.h:253: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 = T; _Args = {int, double, const char (&)[4]}; _Alloc = std::allocator<T>; typename std::enable_if<std::allocator_traits<_Alloc>::__construct_helper<_Tp, _Args>::value, void>::type = void]’
/usr/include/c++/4.7/bits/alloc_traits.h:390:4:   required from ‘static void std::allocator_traits<_Alloc>::construct(_Alloc&, _Tp*, _Args&& ...) [with _Tp = T; _Args = {int, double, const char (&)[4]}; _Alloc = std::allocator<T>]’
/usr/include/c++/4.7/bits/vector.tcc:97:6:   required from ‘void std::vector<_Tp, _Alloc>::emplace_back(_Args&& ...) [with _Args = {int, double, const char (&)[4]}; _Tp = T; _Alloc = std::allocator<T>]’
./test.cpp:17:32:   required from here
/usr/include/c++/4.7/ext/new_allocator.h:110:4: error: no matching function for call to ‘T::T(int, double, const char [4])’
/usr/include/c++/4.7/ext/new_allocator.h:110:4: note: candidates are:
./test.cpp:6:8: note: T::T()
./test.cpp:6:8: note:   candidate expects 0 arguments, 3 provided
./test.cpp:6:8: note: T::T(const T&)
./test.cpp:6:8: note:   candidate expects 1 argument, 3 provided
./test.cpp:6:8: note: T::T(T&&)
./test.cpp:6:8: note:   candidate expects 1 argument, 3 provided

이를 수행하는 올바른 방법은 무엇이며 그 이유는 무엇입니까?

(또한 단일 및 이중 중괄호 시도)



답변

미래의 누구에게나이 동작 C ++ 20 에서 변경 될 것 입니다.

즉, 구현이 내부적으로 여전히 호출 되더라도 예상 할 수있는 T(arg0, arg1, ...)규칙적인 것으로 간주됩니다 T{arg0, arg1, ...}.


답변

클래스에 대한 ctor를 명시 적으로 정의해야합니다.

#include <string>
#include <vector>

using namespace std;

struct T
{
    int a;
    double b;
    string c;

    T(int a, double b, string &&c)
        : a(a)
        , b(b)
        , c(std::move(c))
    {}
};

vector<T> V;

int main()
{
    V.emplace_back(42, 3.14, "foo");
}

사용의 요점은 emplace_back임시 객체를 생성하지 않고 대상으로 복사 (또는 이동)하는 것입니다. 임시 객체를 만든 다음에 전달하는 것도 가능하지만 emplace_back(적어도 대부분의) 목적을 무효화합니다. 원하는 것은 개별 인수를 전달한 다음 emplace_back해당 인수로 ctor를 호출하여 제자리에 객체를 생성하는 것입니다.


답변

물론 이것은 대답이 아니지만 튜플의 흥미로운 기능을 보여줍니다.

#include <string>
#include <tuple>
#include <vector>

using namespace std;

using T = tuple <
    int,
    double,
    string
>;

vector<T> V;

int main()
{
    V.emplace_back(42, 3.14, "foo");
}


답변

생성자를 추가하고 싶지 않거나 추가 할 수없는 경우 T에 대한 할당자를 전문화하거나 고유 한 할당자를 만듭니다.

namespace std {
    template<>
    struct allocator<T> {
        typedef T value_type;
        value_type* allocate(size_t n) { return static_cast<value_type*>(::operator new(sizeof(value_type) * n)); }
        void deallocate(value_type* p, size_t n) { return ::operator delete(static_cast<void*>(p)); }
        template<class U, class... Args>
        void construct(U* p, Args&&... args) { ::new(static_cast<void*>(p)) U{ std::forward<Args>(args)... }; }
    };
}

참고 : 위에 표시된 멤버 함수 구성은 clang 3.1로 컴파일 할 수 없습니다 (죄송합니다. 이유를 모르겠습니다). clang 3.1 (또는 다른 이유)을 사용하려면 다음을 시도하십시오.

void construct(T* p, int a, double b, const string& c) { ::new(static_cast<void*>(p)) T{ a, b, c }; }


답변

이것은 23.2.1 / 13에서 다루는 것 같습니다.

첫째, 정의 :

A와 동일한 allocator_type 및 T와 동일한 value_type을 가지며 A 유형의 lvalue m, 유형 T *의 포인터 p, 유형 T의 표현식 v 및 유형 T의 rvalue rv를 갖는 컨테이너 유형 X가 주어지면, 다음 용어가 정의됩니다.

이제 emplace-constructible로 만드는 것은 무엇입니까?

T는 args에서 X로 EmplaceConstructible입니다. 인수가 0 개 이상인 경우 args는 다음 표현식이 잘 구성되었음을 의미합니다. allocator_traits :: construct (m, p, args);

마지막으로 구성 호출의 기본 구현에 대한 참고 사항 :

참고 : 컨테이너는 allocator_traits :: construct (m, p, args)를 호출하여 args를 사용하여 p에서 요소를 생성합니다. std :: allocator의 기본 구조는 :: new ((void *) p) T (args)를 호출하지만 특수 할당자는 다른 정의를 선택할 수 있습니다.

이것은 기본 (그리고 잠재적으로 유일한) 할당 자 체계의 경우 컨테이너에 배치하려는 항목에 대해 적절한 수의 인수를 사용하여 생성자를 정의 해야 함 을 거의 알려줍니다 .


답변

사소하지 않은를 T포함하고 있기 때문에 유형에 대한 생성자를 정의해야합니다 std::string.

더욱이, 이동 ctor / 할당을 정의하는 것이 더 좋을 것입니다 (기본적으로 가능) 이동 ctor / 할당 ( std::string멤버로서 이동식이 있기 때문에 )-이것은 T훨씬 더 효율적 으로 이동하는 데 도움이 될 것입니다 .

또는 neighboug 응답에서 권장하는대로 T{...}오버로드 emplace_back()된 호출에 사용 합니다 … 모든 것은 일반적인 사용 사례에 따라 다릅니다 …


답변

struct T인스턴스를 만든 다음 벡터로 이동할 수 있습니다 .

V.push_back(std::move(T {42, 3.14, "foo"}));