문제

C ++ (암시 적 또는 명시 적) 값 생성자는 인수의 사본을 객체에 어떤 식 으로든 저장해야 할 때 값 또는 참조로 매개 변수를 수용해야합니까?

다음은 내가 생각할 수있는 가장 짧은 예입니다.

struct foo {
    bar _b;
    foo(bar [const&] b) // pass by value or reference-to-const?
        : _b(b) { }
};

여기서 아이디어는 Foo 객체가 생성 될 때 Foo 객체가 생성 될 수있는 다양한 방법으로 Bar의 카피 생성자에 대한 호출을 최소화하고 싶다는 것입니다.

COPY ELISION 및 (명명 된) 반품 값 최적화에 대해 조금 알고 있으며 읽었습니다. "원한 속도? 가치별로 통과", 그러나이 기사 가이 유스 케이스를 직접 다루는 것으로 생각하지 않습니다.

편집하다: 나는 더 구체적이어야한다.

내가 알 수 없다고 가정합니다 sizeof(bar), 또는 또는 아니에요 bar 기본의 내장 유형입니다 (bar 템플릿 매개 변수 일 수 있습니다 foo 클래스 대신 클래스 템플릿 일 수 있습니다). 또한 그렇게 가정하지 마십시오 foo의 생성자는 상거할 수 있습니다 (또는 bar그 문제에 대해). 적어도 RVO를 구현하는 컴파일러를 사용하고 있다고 가정하십시오.

내가 원하는 것은 이와 같은 통화가 전화를받지 않을 가능성 (컴파일러 최적화)이있을 가능성이 있다는 것입니다. bar(실행할 때도 사본 생성자 _b(b) 안에 foo초기화 목록) :

foo f = function_that_creates_and_returns_a_bar_object_using_rvo();

이 작업을 수행 할 수있는 가능성 (C ++ 98 표준)이 있습니까? 그렇다면 작동 할 가능성이 다소 작동 할 가능성이 높습니까? foo 값 대신 참조-콘스트별로 매개 변수를 허용합니까?

도움이 되었습니까?

해결책

C ++ 98 및 C ++ 03에서는 통과해야합니다. const& bar 그런 다음 복사하십시오. C ++ 0x에서는 통과해야합니다 bar 그런 다음 이동 (제공됩니다 bar 이동 생성자가 있습니다).

#include <utility>

struct foo
{
    bar _b;

    foo(bar b) : _b(std::move(b)) {}
};

lvalue 매개 변수로 foo를 작성하면 사본 생성자가 호출되어 사본을 작성합니다. b, 그리고 그 사본은 안으로 이동합니다 _b. rvalue 매개 변수로 foo를 만들면 bar움직임 생성자가 이동하도록 요청됩니다. b, 그런 다음 다시 이동합니다 _b.

다른 팁

모든 것이 평등하게, 나는 Const와 충분히 복잡한 수업을 위해 POD와 간단한 물체에 대한 가치를 통과합니다.

전통적인 통과 대신 Const 참조를 통과 할 장단점을 배치

긍정적 :

  • 사본을 피하십시오 (비싼 사본이있는 개체의 큰 플러스)
  • 읽기 전용 액세스

부정 :

  • 누군가가 정말로 원한다면 Const를 참조에서 캐스트 할 수 있습니다.

더 중요한 것은 긍정적 인 것이 당신입니다 설명 적으로 사본이 발생할 때 제어 (귀하의 경우 초기 초기화 _B를 초기화 할 때 _B를 통과시). 부정적인 생각에 대해 생각합니다 ... 나는 그것이 위험에 동의합니다. 나는 거의 모든 좋은 프로그래머가 const_cast를 홍보하는 것에 대해 더러워 질 것이라고 생각합니다. 또한 Const_cast를 정중하게 검색하고 논쟁에서 Const를 캐스팅하는 사람에게 토마토를 던질 수 있습니다. 하지만 당신은 결코 알지 못하고 누가 호크처럼 코드를 볼 시간이 있습니다 :)?

저의 주관적인 의견은 충분히 복잡한 클래스와 성능이 중요한 환경에서 카피 생성자를 피할 수있는 이점이 위험을 능가한다는 것입니다. 그러나 정말 바보 같은 수업과 포드 클래스의 경우 데이터 사본을 만들고 가치를 통과하는 경향이 있습니다.

이 봐 의문.

사용 const T & arg 만약에 sizeof(T)>sizeof(void*) 그리고 사용 T arg 만약에 sizeof(T) <= sizeof(void*). 모든 기본 유형은이 규칙의 예외 여야합니다.

문체 적으로, 나는 참조로 통과하는 것이 더 나은 방법이라고 말하고 싶습니다.

성능이 실제로 중요하다면 추측하지 마십시오. 측정하십시오.

나는 표준의 말을 확인하지 않았지만,이 경험적으로 시도함으로써 생성자가 값 또는 const 참조에 의해 인수를 받아 들인지 여부에 관계없이 GCC가 사본을 최적화하지 않는다고 말할 수 있습니다.

그러나 생성자가 Const Reference를 사용하면 생성 할 때 불필요한 사본을 피할 수 있습니다. foo 기존에서 술집 물체.

요약:

Bar b = makeBar();         // No copy, because of RVO
FooByValue f1 = makeBar(); // Copy constructor of Bar called once
FooByRef f2 = makeBar();   // Copy constructor of Bar called once
FooByValue f3(b);          // Copy constructor of Bar called twice
FooByRef f4(b);            // Copy constructor of Bar called only once

나는 컴파일러 전문가라는 것이 아니라 일반적으로 반환 값을 임의의 장소 (예 : foo 물체). 대신 대상이 스택 상단에 있어야합니다.

나는 가정하고있다 bar 양식의 정상적인 사본 생성자가 있습니다 bar(const bar &b)

여기에서 참조를 참조하십시오. bar그런 다음 해당 참조를 사용하여 사본을 수행합니다. 총 사본 : 1.

참조를 꺼내면 컴파일러는 B의 사본을 만들고 사본을 전달합니다. foo의 생성자, 그런 다음 전달할 것입니다. bar 그리고 그것을 복사합니다.

솔직히이 경우 가장 좋은 옵션은 생성자를 만드는 것입니다. inline, 제공 bar의 생성자는 던지지 않습니다. 그런 다음 참조로 통과하십시오.

또한 의심되는대로 기사는 여기에 적용되지 않습니다.

Shared_pointer를 보관하여 클래스에서 막대를 막아서 전달하십시오. 그렇게하면 절대 사본 생성자를 호출하지 않습니다 :).

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top