2017-01-04 1 views
2

구체적으로 : 직접 목록 초기화 (cppreference.com(3)).std :: make_shared/std :: make_unique가 목록 초기화를 사용하지 않는 이유가 있습니까?

모두 std::make_shared균일 초기화 기능이 C++ 11에 소개되었다. 따라서 힙에 객체를 할당 할 때 집합 초기화을 사용할 수 있습니다 : new Foo{1, "2", 3.0f}. 이는 집계, 포드 등과 같은 생성자가없는 객체를 직접 초기화하는 좋은 방법입니다.

캐주얼 문자를 구조로 선언하는 등의 실제 시나리오를 효율적으로 인수 집합을 제공합니다. 람다는 내 경험에 매우 일반적되었다 :

여기
void foo() 
{ 
    struct LambdaArgs 
    { 
     std::string arg1; 
     std::string arg2; 
     std::string arg3; 
    }; 

    auto args = std::make_shared<LambdaArgs>(LambdaArgs{"1", "2", "3"}); 

    auto lambda = [args] { 
     /// ... 
    }; 

    /// Use lambda 
    /// ... 
} 

std::make_shared는 일반적으로 구현되기 때문에 auto args = std::make_shared<LambdaArgs>("1", "2", "3");가 좋은 있지만 작동하지 않을 수 whould :

template<typename T, typename... Args> 
std::shared_ptr<T> make_shared(Args && ...args) 
{ 
    return std::shared_ptr<T>(new T(std::forward<Args>(args)...)); 
} 
,536,

그래서 우리는 auto args = std::make_shared<LambdaArgs>(LambdaArgs{"1", "2", "3"});에 붙어 있습니다.

std::make_shared으로 해결해야하는 문제는 생성자가없는 개체에 대해서도 계속 유지됩니다. 그리고 해결 방법은 심미적이지 않을뿐만 아니라 덜 효율적입니다.

또 다른 감독입니까, 아니면이 선택을 방어해야 할 몇 가지 이유가 있습니까? 특히, 목록 초기화 솔루션에 어떤 함정이있을 수 있습니까? std::make_unique은 나중에 소개되었으므로 C++ 14에 왜 같은 패턴을 따르는가?

+1

당신의 질문에 대한 대답은 아니지만 해결 방법 : 일반 구조체 대신 튜플을 사용하는 경우 적절한 생성자가 제공되며 더 간결합니다 :'LambdaArgs = std :: tuple '; – Frank

+0

'std :: make_shared' 문제는 객체 생성 후 shared_ptr이 소유권을 갖기 전에 예외가 발생하면 모든 것이 올바르게 정리되었는지 확인하는 문제입니다. 이것으로 사소한 감독처럼 보입니다. – Kevin

+0

당신은이 흥미로운 것을 발견 할 수 있습니다 : http://stackoverflow.com/questions/24234480/stdmake-shared-with-stdinitializer-list – marcinj

답변

3

특히, 목록 초기화 솔루션에는 어떤 함정이있을 수 있습니까?

목록 초기화를 사용하는 일반적인 함정.

예를 들어, 비 initializer_list 생성자 숨기기. make_shared<vector<int>>(5, 2)의 기능은 무엇입니까? 귀하의 대답은 "5 int의 배열을 구성하는 경우", 그건 절대 올바른 ... 한도 make_shared목록 초기화를 사용하지 않는 한. 그것은 당신이하는 순간을 변화시키기 때문입니다.

갑자기이 값을 변경하면 은 기존 코드 인을 깨뜨릴 수 있습니다. 간접 초기화 함수는 모두 생성자 구문을 사용하기 때문에주의하십시오. 따라서 당신은 그것을 방탕하게 바꾸고 세계가 계속 일할 것을 기대할 수 없습니다. 이 경우 고유

플러스 하나 더 : 네 로우 문제 :

struct Agg 
{ 
    char c; 
    int i; 
}; 

이 집계를 초기화 Agg a{5, 1020}; 할 수 있습니다. 하지만 절대 할 수는 없어 make_shared<Agg>(5, 1020). 왜? 컴파일러는 5이 데이터 손실없이 char으로 변환 될 수 있음을 보장 할 수 있으므로 그러나 이와 같이 간접 초기화를 사용하면 문자 5은 템플릿으로 추론되어 int으로 추론됩니다. 그리고 컴파일러 어떤 int 데이터의 손실없이 char로 변환 할 수 없습니다 보장 할 수 없습니다. 이를 "축소 변환"이라고하며 목록 초기화시 명시 적으로 금지됩니다.

명시 적으로 해당 5char으로 변환해야합니다.

표준 라이브러리에이 문제가 발생했습니다 : LWG 2089. 기술적으로이 문제는 allocator::construct에 관한 것이지만 any/optional/variant에 대한 make_X 및 C++ 17 내부 위치 생성자와 같은 모든 간접 초기화 함수에도 똑같이 적용되어야합니다.

왜 같은 패턴을 따르고 있습니까?

완전히 똑같아 보이는 두 가지 기능이 근본적으로 예기치 않게 다른 동작을하는 것이 좋지 않기 때문에 동일한 패턴을 따릅니다.

+0

BTW casual 구조체는 * aggregate *의 대체물이 아니며, 좁은 컨텍스트 (* 함수 본문 *) 내에서 한 번만 사용하도록 선언 된 구조체를 생각해내는 이름입니다. – GreenScape

1

std :: make_shared로 해결해야하는 문제는 생성자가없는 개체에 대해서도 계속 유지됩니다.

아니요, 문제가 지속되지 않습니다. 주된 문제 make_shared은 객체가 할당되고 소유권이 스마트 포인터에 의해 취해지는 사이에 메모리 누수가 발생할 수있는 문제를 해결합니다. 또한 제어 블록에 대해 하나의 추가 할당을 제거 할 수도 있습니다.

예, 직접 초기화를 사용하지 못하는 것은 불편하지만 선언 된 목표는 결코 make_shared입니다.

+1

make_shared는 메모리 누수와 아무 관련이 없습니다. make_shared가 제공하는 것은 레퍼런스 카운트와 객체 사이의 참조 지역을 보장하여 역 참조를 할 때 추가 캐시 미스를 피할 수 있습니다. – Frank

+1

@ 프랭크, 아마도 당신은 여기를보고 자신을 교육 할 수 있습니다 : http://en.cppreference.com/w/cpp/memory/shared_ptr/make_shared – SergeyA

+1

나는 고쳐 놨어. 참조 주셔서 감사합니다! 편집 : 귀하의 링크가 여분의 할당을 피하는 것이 주된 이유이며, 메모리 누출을 피하는 것이 좋은 이익이라고 말하면서, 나는 어리석은 느낌이 들지 않습니다. – Frank

관련 문제