developer tip

C ++ 11에서 const 참조가 값에 의한 전달보다 나은 경우는 언제입니까?

copycodes 2020. 12. 24. 23:51
반응형

C ++ 11에서 const 참조가 값에 의한 전달보다 나은 경우는 언제입니까?


const참조를 사용 하여 vector's lot' 과 같은 큰 매개 변수를 전달 하는 C ++ 11 이전 코드가 있습니다. 예는 다음과 같습니다.

int hd(const vector<int>& a) {
   return a[0];
}

새로운 C ++ 11 기능을 사용하면 vector성능 저하없이 다음과 같이 값을 전달할 수 있다고 들었습니다 .

int hd(vector<int> a) {
   return a[0];
}

예를 들어, 이 답변

C ++ 11의 이동 의미론은 복잡한 객체의 경우에도 값에 의한 전달 및 반환을 훨씬 더 매력적으로 만듭니다.

위의 두 옵션이 성능면에서 동일하다는 것이 사실입니까?

그렇다면 언제 옵션 1에서와 같이 const 참조를 옵션 2보다 더 잘 사용합니까? (즉, C ++ 11에서 여전히 const 참조를 사용해야하는 이유).

내가 묻는 한 가지 이유는 const 참조가 템플릿 매개 변수의 추론을 복잡하게 만들고 const 참조 성능면에서 동일하다면 값에 의한 전달 만 사용하는 것이 훨씬 더 쉬울 것입니다.


가치 전달에 대한 일반적인 경험 법칙은 어쨌든 복사본 을 만들 때 입니다. 즉, 이렇게하기보다는 다음과 같이 말합니다.

void f(const std::vector<int>& x) {
    std::vector<int> y(x);
    // stuff
}

먼저 const-ref를 전달한 다음 복사하면 대신 다음을 수행해야합니다.

void f(std::vector<int> x) {
    // work with x instead
}

이것은 C ++ 03에서 부분적으로 사실이었고 , 함수가 rvalue로 호출 될 때 pass-by-val 케이스에서 사본 이동으로 대체 될 수 있기 때문에 이동 의미론에서 더 유용 해졌습니다 .

그렇지 않으면 데이터를 읽는 const것이 원하는 경우 참조로 전달하는 것이 여전히 선호되고 효율적인 방법입니다.


큰 차이가 있습니다. vector죽기 직전이 아니라면의 내부 배열의 복사본을 얻게됩니다 .

int hd(vector<int> a) {
   //...
}
hd(func_returning_vector()); // internal array is "stolen" (move constructor is called)
vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8};
hd(v); // internal array is copied (copy constructor is called)

C ++ 11과 rvalue 참조의 도입으로 벡터와 같은 객체를 반환하는 규칙이 변경되었습니다. 이제 그렇게 할 수 있습니다 (보장 된 복사본에 대해 걱정하지 않고). 그러나 인수가 변경된 것으로 간주 하는 기본 규칙은 없습니다. 실제로 실제 복사본이 필요하지 않는 한 const 참조로 가져와야합니다.


C ++ 11의 이동 의미론은 복잡한 객체의 경우에도 값에 의한 전달 및 반환을 훨씬 더 매력적으로 만듭니다.

그러나 귀하가 제공하는 샘플은 가치 전달의 샘플입니다.

int hd(vector<int> a) {

따라서 C ++ 11은 이것에 영향을 미치지 않습니다.

rvalue를 받기 위해 'hd'를 올바르게 선언 했더라도

int hd(vector<int>&& a) {

값에 의한 전달보다 저렴할 수 있지만 성공적인 이동 ( std::move효과가 전혀 없을 수 있는 단순한 것과는 반대로)을 수행하는 것은 단순한 참조에 의한 전달보다 더 비쌀 수 있습니다. 새 파일 vector<int>을 구성해야하며의 콘텐츠에 대한 소유권을 가져야합니다 a. 새 요소 배열을 할당하고 값을 복사해야하는 이전 오버 헤드는 없지만 여전히 .NET의 데이터 필드를 전송해야합니다 vector.

더 중요한 것은 성공적인 이동의 a경우이 프로세스에서 파괴된다는 것입니다.

std::vector<int> x;
x.push(1);
int n = hd(std::move(x));
std::cout << x.size() << '\n'; // not what it used to be

다음 전체 예를 고려하십시오.

struct Str {
    char* m_ptr;
    Str() : m_ptr(nullptr) {}
    Str(const char* ptr) : m_ptr(strdup(ptr)) {}
    Str(const Str& rhs) : m_ptr(strdup(rhs.m_ptr)) {}
    Str(Str&& rhs) {
      if (&rhs != this) {
        m_ptr = rhs.m_ptr;
        rhs.m_ptr = nullptr;
      }
    }
    ~Str() {
      if (m_ptr) {
        printf("dtor: freeing %p\n", m_ptr)
        free(m_ptr);
        m_ptr = nullptr;
      }
    }
};

void hd(Str&& str) {
  printf("str.m_ptr = %p\n", str.m_ptr);
}

int main() {
  Str a("hello world"); // duplicates 'hello world'.
  Str b(a); // creates another copy
  hd(std::move(b)); // transfers authority for b to function hd.
  //hd(b); // compile error
  printf("after hd, b.m_ptr = %p\n", b.m_ptr); // it's been moved.
}

일반적으로 :

  • 사소한 개체에 대한 값으로 전달,
  • 목적지에 변경 가능한 사본이 필요한 경우 값으로 전달하십시오.
  • 항상 복사본을 만들어야하는 경우 값 으로 전달하십시오.
  • Pass by const reference for non-trivial objects where the viewer only needs to see the content/state but doesn't need it to be modifiable,
  • Move when the destination needs a mutable copy of a temporary/constructed value (e.g. std::move(std::string("a") + std::string("b"))).
  • Move when you require locality of the object state but want to retain existing values/data and release the current holder.

Remember that if you are not passing in an r-value, then passing by value would result in a full blown copy. So generally speaking, passing by value could lead to a performance hit.


Your example is flawed. C++11 does not give you a move with the code that you have, and a copy would be made.

However, you can get a move by declaring the function to take an rvalue reference, and then passing one:

int hd(vector<int>&& a) {
   return a[0];
}

// ...
std::vector<int> a = ...
int x = hd(std::move(a));

That's assuming that you won't be using the variable a in your function again except to destroy it or to assign to it a new value. Here, std::move casts the value to an rvalue reference, allowing the move.

Const references allow temporaries to be silently created. You can pass in something that is appropriate for an implicit constructor, and a temporary will be created. The classic example is a char array being converted to const std::string& but with std::vector, a std::initializer_list can be converted.

So:

int hd(const std::vector<int>&); // Declaration of const reference function
int x = hd({1,2,3,4});

And of course, you can move the temporary in as well:

int hd(std::vector<int>&&); // Declaration of rvalue reference function
int x = hd({1,2,3,4});

ReferenceURL : https://stackoverflow.com/questions/24543330/when-is-a-const-reference-better-than-pass-by-value-in-c11

반응형