const 객체를 반환해야합니까?


78

Effective C++항목 03 에서는 가능하면 const를 사용하십시오.

class Bigint
{
  int _data[MAXLEN];
  //...
public:
  int& operator[](const int index) { return _data[index]; }
  const int operator[](const int index) const { return _data[index]; }
  //...
};

const int operator[]에서 메이크업의 차이를 않습니다 int& operator[].

하지만 어떨까요?

int foo() { }

const int foo() { }

그들은 같은 것 같습니다.

내 질문은 왜 우리가 const int operator[](const int index) const대신 사용 int operator[](const int index) const합니까?


4
좋은 질문입니다.이 경우 메서드는 일반적으로 두 경우 모두 참조를 반환하지 않습니까?
Ken Wayne VanderLinde

좋은 질문. 나는 당신이 그것을 변경 제안 int operator[](int i)const int operator[](const int i).
Alexander Chertov

1
@KenWayneVanderLinde, 아니요. 호출되는 메서드는 Bigint&또는 a const Bigint&.
Alexander Chertov

1
@AlexanderChertov 아니오 : 그는 인수 유형이 아니라 반환 유형에 대해 묻습니다. 그래서 질문은 이것에만 초점을 맞춰야합니다
stijn

왜? 나는 개인적으로 왜 누군가가 const int i논쟁을 벌 일지 궁금합니다.
Alexander Chertov

답변:


88

클래스가 아닌 유형의 리턴 유형에 대한 최상위 레벨 cv 한정자는 무시됩니다. 즉, 다음과 같이 작성하더라도 다음과 같습니다.

int const foo();

반환 유형은 int입니다. 물론 반환 유형이 참조이면 const은 더 이상 최상위 수준이 아니며 다음과 같은 차이점이 있습니다.

int& operator[]( int index );

int const& operator[]( int index ) const;

중요합니다. (위와 같이 함수 선언에서 최상위 레벨 cv 한정자도 무시됩니다.)

이 구분은 클래스 유형의 반환 값과도 관련이 있습니다.를 반환 T const하면 호출자는 반환 된 값에 대해 상수가 아닌 함수를 호출 할 수 없습니다. 예 :

class Test
{
public:
    void f();
    void g() const;
};

Test ff();
Test const gg();

ff().f();             //  legal
ff().g();             //  legal
gg().f();             //  **illegal**
gg().g();             //  legal

6
ff와 gg는 Test를 반환하는 함수로 선언됩니다. 그렇다면 호출은 ff (). f () 등이어야합니다. ff가 Test 클래스의 인스턴스 인 경우 선언에서 괄호를 제거해야합니다 (+1 그러나 :)
user396672 aug

@ user396672 (그리고 BJ ...) 예. 나는 ()ffgg. 추가하겠습니다. 수정 해주셔서 감사합니다.
James Kanze

"구별은 클래스 유형의 반환 값과도 관련이 있습니다"이후의 모든 항목에 +1. 이 응용 프로그램 const은 유용하지만 많은 사람들이이를 간과하거나 const값 을 반환하는 것이 유용하지 않다고 주장하는 것 같습니다 .
underscore_d

4
−1 “비 클래스 유형의 반환 유형에 대한 최상위 수준 cv 한정자는 무시됩니다.”가 잘못되었습니다. 그것들은 여전히 ​​함수 유형의 일부입니다. 그러나 클래스가 아닌 유형의 prvalue에 대한 cv-qualification은 무시되므로를 반환하는 함수 int const호출은 return으로 수정 된 해당 함수 호출과 동일합니다 int. Standardese in C ++ 11 §3.10 / 4,“Class prvalues는 cv-qualified 유형을 가질 수 있습니다. 클래스가 아닌 prvalue는 항상 cv-unqualified 유형을 갖습니다.
건배와 hth. -Alf

38

반환 값, 매개 변수 및 함수 자체에 적용되는 const 사용을 명확하게 구분해야합니다.

반환 값

  • 함수가 value 로 반환되면 객체의 복사본이 반환되므로 const는 중요하지 않습니다. 그러나 이동 의미가 포함 된 C ++ 11에서는 중요합니다.
  • 기본 유형은 항상 복사되므로 중요하지 않습니다.

고려하십시오 const std::string SomeMethod() const. (std::string&&)상수가 아닌 rvalue를 예상 하므로 함수를 사용할 수 없습니다 . 즉, 반환 된 문자열은 항상 복사됩니다.

  • 함수가 반환하는 경우 참조 , const수정되는 반환 된 객체를 보호합니다.

매개 변수

  • 으로 매개 변수를 전달 하면 const는 함수의 함수 의해 주어진 값이 수정되는 것을 방지합니다 . 복사본 만 있기 때문에 매개 변수의 원본 데이터는 수정할 수 없습니다.
  • 복사본은 항상 생성되기 때문에 const는 function body에 대한 의미 만 가지 므로 선언 (인터페이스)이 아닌 함수 정의에서만 확인됩니다.
  • 참조 로 매개 변수를 전달하면 반환 값과 동일한 규칙이 적용됩니다.

기능 자체

  • 함수가 const끝에 있으면 다른 const함수 만 실행할 수 있으며 클래스 데이터를 수정하거나 수정할 수 없습니다. 따라서 참조로 반환되는 경우 반환되는 참조는 const 여야합니다. const 함수 만 객체 또는 const 자체 인 객체에 대한 참조에서 호출 할 수 있습니다. 또한 변경 가능한 필드를 변경할 수 있습니다.
  • 컴파일러에 의해 생성 된 동작은 this참조를 T const*. 이 함수는 항상 할 수 const_cast this있지만 물론이 작업을 수행해서는 안되며 안전하지 않은 것으로 간주됩니다.
  • 물론 클래스 메서드에서만이 지정자를 사용하는 것이 합리적입니다. 끝에 const가있는 전역 함수는 컴파일 오류를 발생시킵니다.

결론

메서드가 클래스 변수를 수정하지 않고 절대로 수정하지 않는 경우이를 const로 표시하고 필요한 모든 기준을 충족해야합니다. 더 깨끗한 코드를 작성할 수 있으므로 const-correct 를 유지할 수 있습니다. 그러나 const아무 생각도하지 않고 어디에나 두는 것은 확실히 갈 길이 아닙니다.


4
두 가지 수정 사항 : 첫째, 값에 의한 반환의 경우 클래스가 아닌 유형에 대해 최상위 수준 const이 무시 됩니다 . 클래스 유형에 따라 중요합니다. 값으로 전달 된 매개 변수의 경우 const함수 선언에서 최상위 수준 이 무시됩니다. 정의에만 의미가 있습니다.
James Kanze

괜찮 으시면이 의견을 제 답변에 포함하겠습니다. 그러면 훨씬 더 명확해질 것 같습니다.
Bartek Banachewicz

const함수에 관해서는 : 대신 this, 의 유형을 수정합니다 . 다른 모든 효과는 이것에서 비롯됩니다. (그리고 객체의 모든 수정을 막지는 않습니다. 데이터는 여전히 변경 될 수 있으며 함수는 합법적으로 const를 버리고 원하는 것을 변경할 수 있습니다.)T const*T*mutable
James Kanze

false "를 참조하여 함수가 반환하는 경우, 수정되는 반환 된 객체 보호"
NoSenseEtAl

아야, ", [const]는 객체를 보호합니다"라는 뜻입니다. 더 읽기 쉽게 수정하겠습니다.
Bartek Banachewicz

30

비 참조 / 비 포인터 rvalue에 자격을 추가하는 데는 별 가치없으며 내장에 추가 할 필요도 없습니다 .const

의 경우에 , 사용자 정의 형 하는 const자격 호출자 막을 비 호출 const멤버 함수를 반환 개체. 예를 들어, 주어진

const std::string foo();
      std::string bar();

그때

foo().resize(42);

금지되는 반면

bar().resize(4711);

허용됩니다.

와 같은 내장 기능의 경우 int이러한 rvalue는 어쨌든 수정할 수 없기 때문에 전혀 의미가 없습니다.

(하지만 효과적인 C ++operator=() 에서 const참조 의 반환 유형을 만드는 데 대해 논의한 것을 기억 합니다. 이것은 고려해야 할 사항입니다.)


편집하다:

Scott이 실제로 그 조언을 한 것 같습니다 . 그렇다면 위에 주어진 이유 때문에 C ++ 98 및 C ++ 03에서도 의심 스럽습니다 . C ++ 11의 경우 Scott 자신이 발견 한 것처럼 분명히 잘못되었다고 생각합니다 . 효과적인 C ++정오표 , 3 판. , 그는 다음과 같이 씁니다 (또는 불평 한 다른 사람을 인용합니다).

텍스트는 모든 값별 반환 값이 const 여야 함을 의미하지만, 상수가 아닌 값별 반환 값이 좋은 디자인 인 경우 찾기가 어렵지 않습니다. 예를 들어 호출자가 빈 벡터와 함께 스왑을 사용하는 std :: vector 반환 유형 반환 값 내용을 복사하지 않고 "잡기".

그리고 나중에 :

값별 함수 반환 값 const를 선언하면 C ++ 0x의 rvalue 참조에 바인딩되지 않습니다. rvalue 참조는 C ++ 코드의 효율성을 향상시키는 데 도움이되도록 설계되었으므로 함수 서명을 지정할 때 const 반환 값의 상호 작용과 rvalue 참조의 초기화를 고려하는 것이 중요합니다.


1
나는 Scott의 조언과 함께 정오표에 대한 언급을 좋아합니다. 효과적인 C ++를 처음 접하는 사람들에게 혼란 스러울 수있는 부분을 밝히는 데 정말 도움이됩니다
sehe aug

2
@sehe : 나는 Scott에게이 문제에 대해 묻는 메일을 보냈고 그의 대답은 그 정오표가 문제에 대한 그의 현재 생각을 반영한다는 것입니다.
sbi

17

마이어스의 조언을 놓칠 수도 있습니다. 근본적인 차이점은 const방법에 대한 수정 자입니다.

이것은 non-const 메소드 ( const끝에 주의 없음 )로 클래스의 상태를 수정할 수 있음을 의미합니다.

int& operator[](const int index)

이것은 const 메서드입니다 ( const마지막에주의)

const int operator[](const int index) const

매개 변수 및 반환 값의 유형에 대해,이 사이에 약간의 차이는 무엇 int하고 const int있지만, 조언의 점 관련이 없습니다. non-const 오버로드가 반환된다는 점에주의해야 할 점은 int&예를 들어 할당 할 수 있음을 의미 num[i]=0하며, const 오버로드는 (반환 유형이 int또는 이든 관계없이) 수정할 수없는 값을 반환합니다 const int.

제 개인적인 의견으로는 객체가 value 로 전달 되면 constmodifier는 불필요합니다. 이 구문은 더 짧고 동일한 결과를 얻습니다.

int& operator[](int index);
int operator[](int index) const;

5
실제로 문제는 const값을 반환하는 것 입니다.
juanchopanza

@juanchopanza : OP가 정확히 그런 의미인지 의심합니다. 그의 foo예를 예와 비교하십시오 operator[]. 그는 const메소드에 대한 수정자가 리턴 유형이 아니라 차이를 만드는 점을 놓칠 수 있습니다 .
Andrey

13

값을 const로 반환하는 주된 이유는 다음과 같이 말할 수 없기 때문 foo() = 5;입니다. 당신이 기본 유형의 우변에 할당 할 수 없습니다 때문에 실제로 기본 형식에 문제가 아니지만, 그것은 이다 사용자 정의 유형 (같은과 관련된 문제 (a + b) = c;,가 과부하로 operator+).

나는 항상 다소 어설픈 이유를 찾았습니다. 어색한 코드를 작성하려는 사람을 막을 수는 없으며, 이러한 특정 유형의 강압은 제 생각에는 실질적인 이점이 없습니다.

C ++ 11에서는 실제로이 관용구가하는 많은 칩니다. const로 값을 반환하면 이동 최적화가 방지되므로 가능하면 피해야합니다. 기본적으로 저는 이것을 안티 패턴이라고 생각합니다.

다음은 C ++ 11에 관한 접선 관련 기사 입니다.


반환 값을 수정하는 것이 포인트라고 말하는 것은 어떻습니까? MyClass.isInitialized (). 호출자가 반환 값을 변경하면 코드 검토에서 WTF로 이동합니다.
NoSenseEtAl

@NoSenseEtAl : 무의미한 것이 뭔지 아는 사람은 누구입니까? 우리가 아는 한, 할당 연산자는 전역 상태를 수정하거나 피자를 주문할 수 있습니다.
Kerrek SB

예를 추가했습니다. 나의 과거 코멘트에. 그리고 당신은 클래스의 디자이너이므로 반환 유형의 연산자 = D를 이해합니다. 따라서 호출자가 메서드의 결과를 변경하는 것이 무의미한 지 알고 있습니다.
NoSenseEtAl

9

그 책이 쓰여졌을 때 그 조언은 거의 쓸모가 없었지만, 예를 들어 사용자가 글을 쓰는 것을 막고 foo() = 42;그것이 지속적인 무언가를 바꿀 것으로 기대하는 역할을했습니다.

의 경우 값 대신 참조 또는 프록시 객체를 반환하여 혼동을 방지 할 수 있지만 비 참조 를 반환하는 비 오버로드 operator[]도 제공하지 않으면 약간 혼란 스러울 수 있습니다 .constconstconst

요즘에는 결과를 (비 const) rvalue 참조에 바인딩하지 못하므로 나쁜 조언 입니다.

(댓글에서 지적했듯이 int언어가에 할당하는 것을 방지하기 때문에. 과 같은 기본 유형을 반환 할 때 질문은 논쟁의 여지 가 있습니다.rvalue 가 그러한 유형에 . 사용자 정의 유형 반환을 포함하는 더 일반적인 경우에 대해 이야기하고 있습니다. )


10
그의 예가 돌아왔다 int. foo() = 42;에 관계없이 반환 형식이 선언 여부, 불법 int또는 int const. 나는 Scott이 const함수 선언의 반환 유형 및 매개 변수와 같은 위치 에서 사용을 주장했다고 ​​생각하지 않습니다 . 컴파일러에서 무시합니다.
James Kanze

@JamesKanze : 지적 해 주셔서 감사합니다. 나는 내가 더 일반적으로 이야기하고 있다는 것을 분명히해야했다.
Mike Seymour

2

기본 유형 (예 int:)의 경우 결과의 불변성은 중요하지 않습니다. 클래스의 경우 동작을 변경할 수 있습니다. 예를 들어 함수의 결과에 대해 상수가 아닌 메서드를 호출하지 못할 수 있습니다.

class Bigint {
    const C foo() const { ... }
     ...
}

Bigint b;
b.foo().bar();

bar()const 멤버 함수 인 경우 위의 내용은 금지됩니다 C. 일반적으로 말이되는 것을 선택하십시오.


0

저것 봐 :

const int operator[](const int index) const

const문에 끝. 이 메서드는 상수에 대해 호출 될 수 있음을 설명합니다.

반면에 쓸 때만

int& operator[](const int index)

상수가 아닌 인스턴스에서만 호출 할 수 있으며 다음도 제공합니다.

big_int[0] = 10;

통사론.


0

오버로드 중 하나는 배열의 항목에 대한 참조 를 반환하는 것입니다. 그러면이를 변경할 수 있습니다.

int& operator[](const int index) { return _data[index]; }

다른 오버로드는 사용할 값을 반환합니다.

const int operator[](const int index) const { return _data[index]; }

이러한 각 오버로드를 동일한 방식으로 호출하고 사용시 값을 변경하지 않습니다.

int foo = myBigInt[1]; // Doesn't change values inside the object.
myBigInt[1] = 2; // Assigns a new value at index `

0

배열 인덱싱 연산자 ( operator[]) 의 예에서는 차이가 있습니다.

int& operator[](const int index) { /* ... */ }

인덱싱을 사용하여 배열의 항목을 직접 변경할 수 있습니다 (예 : 다음과 같이 사용).

mybigint[3] = 5;

두번째, const int operator[](const int) 연산자는 값을 가져 오는 데만 사용됩니다.

그러나 함수의 반환 값으로 예를 들어 간단한 유형의 int경우 중요하지 않습니다. 문제가되는 경우는 a std::vector와 같이 더 복잡한 유형을 반환 하고 함수 호출자가 벡터를 수정하는 것을 원하지 않는 경우입니다.


0

여기서 const반환 유형은 그렇게 중요하지 않습니다. int 임시는 수정할 수 없기 때문에 intvs 를 사용하는 데 눈에 띄는 차이가 없습니다 const int. 수정할 수있는 더 복잡한 개체를 사용하면 차이를 볼 수 있습니다.


0

두 버전의 전문성을 중심으로 몇 가지 좋은 답변이 있습니다. 원시 값의 경우 차이를 만들지 않습니다.

그러나 나는 항상 const컴파일러보다는 프로그래머를 위해 거기에 있다고 생각 했습니다. 를 작성할 때 const"이것은 변경해서는 안된다"고 명시 적으로 말하는 것입니다. 현실을 직시하자,const 어쨌든 일반적으로 우회 할 수 있습니다.

를 반환하면 const해당 함수를 사용하는 프로그래머에게 값이 변경되어서는 안된다고 알리는 것입니다. 그리고 그가 그것을 바꾸고 있다면, 그는 아마도 뭔가 잘못하고있을 것입니다. 왜냐하면 그 / 그녀가 그럴 필요가 없기 때문입니다.

편집 : 또한 " const가능할 때마다 사용 "이 나쁜 조언 이라고 생각 합니다. 의미있는 곳에서 사용해야합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.