복사 할당 연산자가 참조 / 상수 참조를 반환해야하는 이유는 무엇입니까?
C ++에서는 복사 할당 연산자에서 참조를 반환하는 개념이 명확하지 않습니다. 복사 할당 연산자가 새 개체의 복사본을 반환 할 수없는 이유는 무엇입니까? 또한, 클래스 A
및 다음이있는 경우 :
A a1(param);
A a2 = a1;
A a3;
a3 = a2; //<--- this is the problematic line
는 operator=
다음과 같이 정의된다 :
A A::operator=(const A& a)
{
if (this == &a)
{
return *this;
}
param = a.param;
return *this;
}
엄밀히 말하면 복사 할당 연산자의 결과는 참조를 반환 할 필요가 없지만 C ++ 컴파일러가 사용하는 기본 동작을 모방하려면 할당 된 객체에 대한 상수가 아닌 참조 (암시 적으로 생성 된 복사본)를 반환해야합니다. 할당 연산자는 상수가 아닌 참조를 반환합니다 (C ++ 03 : 12.8 / 10). void
복사 할당 오버로드에서 반환 되는 약간의 코드를 보았고 그로 인해 심각한 문제가 발생한시기를 기억할 수 없습니다. 반환 void
하면 사용자가 '할당 체인'(a = b = c;
), 예를 들어 테스트 표현식에서 할당 결과를 사용하지 못하게합니다. 이런 종류의 코드는 결코 들어 본 적이 없지만, 특히 비 원시적 유형의 경우 특히 일반적이라고 생각하지 않습니다 (클래스의 인터페이스가 iostreams와 같은 이러한 종류의 테스트를 의도하지 않는 한).
나는 당신이 이것을 권장하는 것이 아니라 그것이 허용되고 그것이 많은 문제를 일으키지 않는 것 같다는 것을 지적합니다.
이러한 다른 SO 질문은 사용자가 관심을 가질 수있는 정보 / 의견이있는 관련 (아마도 속지 않음)입니다.
값이 반환되면 operator=
체인 a = b = c
이 잘 작동 하므로 참조로 반환하는 것이 더 나은 이유에 대한 약간의 설명 .
참조를 반환하면 최소한의 작업이 수행됩니다. 한 개체의 값이 다른 개체로 복사됩니다.
그러나에 대한 값으로 반환 operator=
하면 할당 연산자가 호출 될 때마다 생성자 및 소멸자를 호출합니다 !!
그래서 주어진 :
A& operator=(const A& rhs) { /* ... */ };
그때,
a = b = c; // calls assignment operator above twice. Nice and simple.
그러나,
A operator=(const A& rhs) { /* ... */ };
a = b = c; // calls assignment operator twice, calls copy constructor twice, calls destructor type to delete the temporary values! Very wasteful and nothing gained!
요컨대, 가치 반환으로 얻은 것은 없지만 잃을 것이 많습니다.
( 참고 : 이것은 할당 연산자가 lvalue를 반환하도록하는 이점을 설명하기위한 것이 아닙니다. 이것이 바람직한 이유는 다른 게시물을 참조하십시오.)
당신이 과부하되면 operator=
, 당신은 할 수 있습니다 당신이 원하는 유형 돌아 물품. 너무 심하게 원할 경우 오버로드 X::operator=
하여 완전히 다른 클래스 Y
또는 Z
. 이것은 일반적으로 매우 바람직하지 않습니다.
특히, 일반적으로 operator=
C와 마찬가지로 연결을 지원하려고합니다 . 예를 들면 :
int x, y, z;
x = y = z = 0;
이 경우 일반적으로 할당되는 유형의 lvalue 또는 rvalue를 반환하려고합니다. X에 대한 참조, X에 대한 const 참조 또는 X (값 기준)에 대한 참조를 반환할지 여부에 대한 질문 만 남습니다.
X에 대한 const 참조를 반환하는 것은 일반적으로 좋지 않습니다. 특히, const 참조는 임시 개체에 바인딩 할 수 있습니다. 임시의 수명은 바인딩 된 참조의 수명으로 확장되지만 할당 될 수있는 항목의 수명에는 재귀 적으로 확장되지 않습니다. 이렇게하면 매달린 참조를 쉽게 반환 할 수 있습니다. const 참조는 임시 객체에 바인딩됩니다. 해당 객체의 수명은 참조의 수명 (함수 끝에서 끝남)으로 확장됩니다. 함수가 반환 될 때까지 참조 및 임시 수명이 종료되었으므로 할당 된 참조는 댕글 링 참조입니다.
Of course, returning a non-const reference doesn't provide complete protection against this, but at least makes you work a little harder at it. You can still (for example) define some local, and return a reference to it (but most compilers can and will warn about this too).
Returning a value instead of a reference has both theoretical and practical problems. On the theoretical side, you have a basic disconnect between =
normally means and what it means in this case. In particular, where assignment normally means "take this existing source and assign its value to this existing destination", it starts to mean something more like "take this existing source, create a copy of it, and assign that value to this existing destination."
From a practical viewpoint, especially before rvalue references were invented, that could have a significant impact on performance--creating an entire new object in the course of copying A to B was unexpected and often quite slow. If, for example, I had a small vector, and assigned it to a larger vector, I'd expect that to take, at most, time to copy elements of the small vector plus a (little) fixed overhead to adjust the size of the destination vector. If that instead involved two copies, one from source to temp, another from temp to destination, and (worse) a dynamic allocation for the temporary vector, my expectation about the complexity of the operation would be entirely destroyed. For a small vector, the time for the dynamic allocation could easily be many times higher than the time to copy the elements.
The only other option (added in C++11) would be to return an rvalue reference. This could easily lead to unexpected results--a chained assignment like a=b=c;
could destroy the contents of b
and/or c
, which would be quite unexpected.
That leaves returning a normal reference (not a reference to const, nor an rvalue reference) as the only option that (reasonably) dependably produces what most people normally want.
It's partly because returning a reference to self is faster than returning by value, but in addition, it's to allow the original semantics that exist in primitive types.
operator=
can be defined to return whatever you want. You need to be more specific as to what the problem actually is; I suspect that you have the copy constructor use operator=
internally and that causes a stack overflow, as the copy constructor calls operator=
which must use the copy constructor to return A
by value ad infinitum.
There is no core language requirement on the result type of a user-defined operator=
, but the standard library does have such a requirement:
C++98 §23.1/3:
” The type of objects stored in these components must meet the requirements of
CopyConstructible
types (20.1.3), and the additional requirements ofAssignable
types.
C++98 §23.1/4:
” In Table 64,
T
is the type used to instantiate the container,t
is a value ofT
, andu
is a value of (possiblyconst
)T
.
Returning a copy by value would still support assignment chaining like a = b = c = 42;
, because the assignment operator is right-associative, i.e. this is parsed as a = (b = (c = 42));
. But returning a copy would prohibit meaningless constructions like (a = b) = 666;
. For a small class returning a copy could conceivably be most efficient, while for a larger class returning by reference will generally be most efficient (and a copy, prohibitively inefficient).
Until I learned about the standard library requirement I used to let operator=
return void
, for efficiency and to avoid the absurdity of supporting side-effect based bad code.
With C++11 there is additionally the requirement of T&
result type for default
-ing the assignment operator, because
C++11 §8.4.2/1:
” A function that is explicitly defaulted shall […] have the same declared function type (except for possibly differing ref-qualifiers and except that in the case of a copy constructor or copy assignment operator, the parameter type may be “reference to non-const
T
”, whereT
is the name of the member function’s class) as if it had been implicitly declared
'IT TIP' 카테고리의 다른 글
Xcode 누락 지원 파일 iOS 12.2 (16E227) (0) | 2020.11.26 |
---|---|
다중 모듈 메이븐 리액터 프로젝트의 루트 디렉토리 찾기 (0) | 2020.11.26 |
다른 컨트롤러에서 부분보기 렌더링 (0) | 2020.11.26 |
make : 규칙 호출 규칙 (0) | 2020.11.26 |
템플릿을 다른 템플릿에 어떻게 삽입합니까? (0) | 2020.11.26 |