2017-12-31 145 views
8

내가이 구조체를 말한다 간단한거야표준 : make_unique의 (그리고 설치하다, emplace_back의) initializer_list 인수에 대한 어색 공제

auto bla = std::make_unique<positioned>({1,2}); 

작동하려면 어떻게해야합니까?

현재 컴파일러는 initializer_list<int>을 통해 일치를 시도하고 positioned에는 하나의 생성자 만 있기 때문에 어리석은 make_unique의 배열 변형을 호출합니다. emplaceemplace_back 함수에도 동일한 문제가 발생합니다. 클래스의 생성자에 가변 인수 템플릿 인수를 전달하는 함수는 대부분이 동작을 나타내는 것으로 보입니다.

는 내가 두 int 인수 생성자를 positioned을주고 명시 적으로 position{1,2}make_unique에 인수의 유형을 지정 make_unique, 또는

  • 로 호출에서 {}을 삭제

    1. 으로이 문제를 해결할 수 있습니다 이해합니다.

    두 가지 모두 지나치게 장황하게 보입니다. (make_unique 구현에서 약간의 노력이 필요함)이 문제는 인수 유형이 지나치게 지정되지 않으면 해결할 수 있습니다.

    make_unique 구현에서 해결할 수있는 결함입니까, 아니면 누구도 신경 쓸 필요가없는 해결하기 어려운 흥미로운 사례입니까?

    +0

    확인. 삭제합시다. –

    답변

    0

    문제는 {1, 2}과 같은 초기화 도구를 추론 할 수 없다는 것입니다. 다음 코드는 작동하지 않습니다 (이유는 무엇입니까? {1, 2} 유형은 무엇입니까?).

    template<typename U> 
    void foo(U args) { } 
    
    foo({1, 2}); 
    

    make_unique은이 주제에서 좀 더 복잡한 변형입니다.

    근본 원인이 여기에 자세히 설명되어 있습니다 : initializer_list and template type deduction

    +1

    문제가 무엇인지 알고 있습니다. 주변에 방법이 있는지 궁금합니다. (나는 희망한다.) 나는 braced init리스트가 new와 make_unique를 호출하는 것 사이에이 비대칭을 없애도록 생성자의 인자 타입과 일치하도록해야한다고 언급 한 경우에 가능해야한다. – rubenvb

    +0

    글쎄, 내가 볼 수있는 유일한 방법은 일반적인 경우에 std :: make_unique로 전달하는 자신 만의'make_unique' 함수를 만드는 것이고, 원하는 유형을 전문화하는 것입니다. (또는 ub로 놀고 직접 std :: make_unique를 전문화 할 수있다 ...) –

    +0

    나는 표준화 된 것들이 허용되었다고 생각 하나? – rubenvb

    1

    지금까지 내가 볼 수 있듯이,이 작업을 수행 할 수있는 가장 실용적인 방법은 괄호를 제거하고, 몰래 인수를 취할 생성자를 추가 얻을 아마 :

    struct position 
    { 
        int x, y; 
    
        position(int x, int y) : x(x), y(y) {} 
    }; 
    
    class positioned 
    { 
    public: 
        positioned(int x, int y) : pos(x, y) {} 
    private: 
        position pos; 
    }; 
    
    int main() { 
        auto bla = std::make_unique<positioned>(1,2); 
    } 
    

    position 경우 하나 이상의 ctor에 있었다, 당신은 아마 어떤 임의의 매개 변수를 사용하고 position의 ctor에 (들)을 통과 positioned에 대한 가변 인자 템플릿의 ctor를 만들 것입니다.

    struct position 
    { 
        int x, y; 
    
        position(int x, int y) : x(x), y(y) {} 
        position(int b) : x(b), y(b) {} // useless--only to demo a different ctor 
    }; 
    
    class positioned 
    { 
    public: 
        template <class... Args> 
        positioned(Args&&... a) : pos(std::forward<Args>(a)...) {} 
    private: 
        position pos; 
    }; 
    
    int main() { 
        auto bla = std::make_unique<positioned>(1,2); // use 1st ctor 
        auto bla2 = std::make_unique<positioned>(1); // use 2nd ctor 
    } 
    

    인수가 positionpositionedmake_unique에서 통해 전달 받기 방법.이렇게하면 효율면에서 적어도 잠재적 인 이점도 있습니다. 임시 객체를 만드는 데 인수를 사용하는 대신 기본 객체를 초기화하기 위해 전달되는 대신 원본 객체를 참조 객체에 직접 전달합니다 (참조). 기본 객체이므로 한 번만 내부에서 구성합니다.

    이것은 우리에게 상당한 융통성을 제공함을 주목하십시오. 예를 들어, positioned을 가정 해 봅시다 자체가 템플릿이 있었고, 기본 position 템플릿 인수 :

    #include <memory> 
    
    struct position2 
    { 
        int x, y; 
    
        position2(int x, int y) : x(x), y(y) {} 
    }; 
    
    struct position3 { 
        int x, y, z; 
    
        position3(int x, int y, int z) : x(x), y(y), z(z) {} 
    }; 
    
    template <class Pos> 
    class positioned 
    { 
    public: 
        template <class... Args> 
        positioned(Args&&... a) : pos(std::forward<Args>(a)...) {} 
    private: 
        Pos pos; 
    }; 
    
    int main() { 
        auto bla = std::make_unique<positioned<position2>>(1,2); 
        auto bla2 = std::make_unique<positioned<position3>>(1, 2, 3); 
    } 
    

    호환성 : 나는 make_unique이 전달/가변 인자의 ctor를 받았을 때 그 이후이, C++ 14 이상이 필요합니다 생각합니다.

    7

    braced-init-list가 주어지면 기능 템플릿 인수 공제가 작동하지 않습니다. 실제 표현을 기반으로 만 작동합니다.

    positioned은 어쨌든 {1, 2}에서 초기화 할 수 없습니다. 이것은 두 개의 인수 생성자를 호출하려고 시도하며 positioned에는 그러한 생성자가 없습니다. positioned({1, 2}) 또는 positioned{{1, 2}}을 사용해야합니다.

    이와 같이, 일반적인 해결책은 make_unique이 어떻게 구성하고있는 유형에 대한 모든 가능한 생성자의 서명을 마술처럼 재생산하는 것입니다. 이것은 분명히 현재로서는 C++에서 합리적인 일이 아닙니다.

    대안은 객체를 생성하는 람다를 사용하고, 다른 make 기능을 사용 C++ 내부 new 표현 반환 prvalue을 적용 생략 규칙을 보장 17 년대 작성하는 것입니다 :

    template<typename T, typename Func, typename ...Args> 
    std::unique_ptr<T> inject_unique(Func f, Args &&...args) 
    { 
        return std::unique_ptr<T>(new auto(f(std::forward<Args>(args)...))); 
    } 
    
    auto ptr = inject_unique<positioned>([]() {return positioned({1, 2});}); 
    

    당신을 typename T 매개 변수를 도랑 수 있습니다 :

    template<typename Func, typename ...Args> 
    auto inject_unique(Func f, Args &&...args) 
    { 
        using out_type = decltype(f(std::forward<Args>(args)...)); 
        return std::unique_ptr<out_type>(new auto(f(std::forward<Args>(args)...))); 
    } 
    
    auto ptr = inject_unique([]() {return positioned({1, 2});}); 
    
    +0

    멍청한 질문 일지 모르지만 왜'make_unique'를 사용하지 않습니까? – Rakete1111

    +0

    @ Rakete1111 : 그것은 복사/이동을 호출하기 때문입니다. 보장 된 elision으로 이런 식으로하는 것은 그렇지 않습니다. 객체를 직접 생성합니다. –

    +0

    나는 복사/이동이 일어나는 곳을 알 수 없다. 'make_unique'는 정확히 똑같은 일을하고 매개 변수를 (포워딩) 참조로 사용하기 때문에 혼란 스럽습니다. 제발 나를 도울 수 :) 감사합니다 – Rakete1111