함수 매개 변수에 'const'사용


397

얼마나 멀리 가니 const? const필요할 때 기능을 합니까, 아니면 전체 돼지 고기를 가지고 어디에서나 사용합니까? 예를 들어, 단일 부울 매개 변수를 사용하는 간단한 변경자를 상상해보십시오.

void SetValue(const bool b) { my_val_ = b; }

const실제로 유용? 개인적으로 매개 변수를 포함하여 광범위하게 사용하기로 선택했지만이 경우 가치가 있는지 궁금합니다.

또한 const함수 선언의 매개 변수에서 생략 할 수 있지만이를 함수 정의에 포함 시킬 수 있다는 사실에 놀랐습니다 .

.h 파일

void func(int n, long l);

.cpp 파일

void func(const int n, const long l)

이것에 대한 이유가 있습니까? 나에게는 조금 특이한 것 같습니다.


동의하지 않습니다. .h 파일에는 const 정의도 있어야합니다. 그렇지 않은 경우 const 매개 변수가 함수에 전달되면 .h 파일의 프로토 타입에 const 정의가 없으므로 컴파일러에서 오류가 발생합니다.
selwyn

10
동의한다. :-) (질문과 함께 마지막 주석이 아닙니다!) 함수 본문에서 값을 변경하지 않아야하는 경우 바보 같은 == 또는 = 버그를 중지하는 데 도움이 될 수 있습니다. 그것은 가치에 의해 전달됩니다, 그렇지 않으면 당신은 그렇지 않으면 안됩니다) 그것은 그것에 대해 논쟁에 들어갈 정도로 심각하지 않습니다!
Chris Huang-Leaver

19
@selwyn : const int를 함수에 전달하더라도 참조가 아니기 때문에 복사 될 것이므로 const-ness는 중요하지 않습니다.
jalf

1
이 질문에서 동일한 토론이 일어나고 있습니다 : stackoverflow.com/questions/1554750/…
Part

5
나는이 글이 몇 살이라는 것을 알고 있지만, 새로운 프로그래머로서이 질문이 궁금해서이 대화에 걸려 넘어졌다. 내 의견으로는, 함수가 참조 또는 값 / 객체의 사본인지 여부에 관계없이 값을 변경해서는 안되는 경우 const이어야합니다. 더 안전하고 자체 문서화되며 디버그에 더 친숙합니다. 하나의 문장이있는 가장 간단한 함수조차도 여전히 const를 사용합니다.

답변:


187

그 이유는 매개 변수의 const가 데이터 사본에서 작업하기 때문에 함수 내에서만 로컬로 적용되기 때문입니다. 이것은 함수 서명이 실제로 동일하다는 것을 의미합니다. 비록 이것을 많이하는 것은 나쁜 스타일 일 것입니다.

개인적으로 참조 및 포인터 매개 변수를 제외하고 const를 사용하지 않는 경향이 있습니다. 복사 된 객체의 경우 함수 내에서 의도를 알리기 때문에 더 안전 할 수 있지만 실제로는 중요하지 않습니다. 실제로 판결 요청입니다. 나는 무언가를 반복 할 때 const_iterator를 사용하는 경향이 있으며 그것을 수정하려고하지 않으므로 참조 유형에 대한 const 정확성이 엄격하게 유지되는 한 각각의 것으로 추측합니다.


57
나는 '나쁜 스타일'부분에 동의하지 않습니다. const함수 프로토 타입에서 삭제 하면 const나중에 구현 부분에서 삭제하기로 결정한 경우 헤더 파일을 변경할 필요가 없다는 이점이 있습니다 .
Michał Górny

4
"개인적으로 참조 및 포인터 매개 변수를 제외하고 const를 사용하지 않는 경향이 있습니다." 어쩌면 "함수 선언에 불필요한 한정자를 사용하지 않고 const유용한 차이를 만드는 곳 에서 사용하는 경향이 있습니다 ."
중복 제거기

3
이 답변에 동의하지 않습니다. 나는 다른 방법으로 몸을 기울이고 const가능할 때마다 매개 변수를 표시 합니다. 더 표현력이 좋습니다. 다른 사람의 코드를 읽을 때, 나는 마법의 숫자, 주석 및 적절한 포인터 사용법 등과 함께 코드를 작성하는 데 얼마나 많은주의를 기울이는 지 판단하기 위해 이와 같은 작은 표시기를 사용합니다.
Ultimater

4
int getDouble(int a){ ++a; return 2*a; }이 시도. 물론, ++a거기에 할 일은 없지만 오랜 시간 동안 한 명 이상의 프로그래머가 작성한 긴 기능에서 찾을 있습니다. int getDouble( const int a ){ //... }찾을 때 컴파일 오류가 발생 하도록 작성 하는 것이 좋습니다 ++a;.
dom_beau

3
누가 어떤 정보를 필요로 하는가는 모두 문제입니다. 매개 변수 를 값으로 제공하면 호출자 자신이 수행 한 작업 (내부)에 대해 아무것도 알 필요가 없습니다 . class Foo { int multiply(int a, int b) const; }헤더에 쓰 십시오. 구현에 당신은주의 할 당신은 변경하지 않겠다고 약속 할 수있는을 a하고 b그래서 int Foo::multiply(const int a, const int b) const { }여기에 의미가 있습니다. (Sidenote : 호출자와 구현 모두 함수가 Foo객체를 변경하지 않기 때문에 선언이 끝날 때 const 가 중요하다는 사실에주의를 기울입니다 )
CharonX

415

"호출자의 객체를 수정하지 않기 때문에 인수가 값으로 전달 될 때 const는 의미가 없습니다."

잘못된.

코드와 가정을 자체 문서화하는 것입니다.

코드에 많은 사람들이 작업하고 함수가 사소한 것이 아니라면 가능한 한 모든 것을 "const"로 표시해야합니다. 산업 강점 코드를 작성할 때 항상 동료가 가능한 한 길을 찾으려고 노력하는 정신병자라고 생각해야합니다 (특히 미래에 종종 자신이기 때문에).

게다가, 앞서 언급 한 누군가 가 컴파일러가 약간 최적화하는 데 도움이 될 수 있습니다 (긴 샷 임에도 불구하고).


41
전적으로 동의합니다. 그것은 사람들과 의사 소통하고 변수로 수행 할 수있는 일을 수행해야 할 일로 제한하는 것입니다.
Len Holgate

19
나는 이것을 아래로 투표했다. 값 인수로 간단한 패스에 적용 할 때 const로 표시하려고하는 것을 희석한다고 생각합니다.
tonylo

26
나는 이것을 투표했다. 'const'매개 변수를 선언하면 의미 정보가 매개 변수에 추가됩니다. 코드의 원래 작성자가 의도 한 내용을 강조 표시하므로 시간이 지남에 따라 코드를 유지 관리하는 데 도움이됩니다.
Richard Corden

13
@ tonylo : 당신은 오해합니다. 이것은 코드 블록 내부에서 지역 변수를 const로 표시하는 것입니다 (함수 발생). 모든 로컬 변수에 대해 동일한 작업을 수행합니다. const-correct 인 API를 갖는 것은 직교 적이며, 실제로도 중요합니다.
rlerallut

56
그리고 함수 에서 버그를 잡을 수 있습니다 . 매개 변수를 변경해서는 안된다는 것을 안다면 const 선언하면 컴파일러가 실수로 수정했는지 알려줍니다.
Adrian

156

때때로 (너무 자주!) 다른 사람의 C ++ 코드를 풀어야합니다. 그리고 우리는 다른 사람의 C ++ 코드가 거의 정의에 의해 완전한 엉망 이라는 것을 알고 있습니다. 따라서 로컬 데이터 흐름을 해독하기 위해 가장 먼저해야 할 일은 컴파일러가 짖기 시작할 때까지 모든 변수 정의 에 const 를 넣는 것 입니다. 이는 한정자 인수를 의미하는데, 이는 호출자가 초기화 한 멋진 로컬 변수이기 때문입니다.

아, 변수가 기본적 으로 const 였고 비 상수 변수에는 변경 이 필요했습니다 :)


4
"변수가 기본적으로 const 였으면 좋겠다"-옥시 모론 ?? 8-) 진지하게, 모든 것을 "consting"하면 어떻게 코드를 풀 수 있습니까? 원 작가가 일정한 주장을 바꾼다면, var가 상수라고 가정 한 것을 어떻게 알 수 있습니까? 더욱이, 인수가 아닌 (non-argument) 변수의 대부분은 변수입니다. 따라서 프로세스를 시작한 직후 컴파일러가 중단되어야합니다.
ysap

8
@ysap, 1. const를 최대한 많이 표시하면 움직이는 부분과 움직이지 않는 부분을 볼 수 있습니다. 내 경험상, 많은 현지인들은 사실상 다른 방식이 아니라 사실상 구성 적입니다. 2. "const variable"/ "Immutable variable"은 옥시 모론처럼 들릴 수 있지만 기능적 언어뿐만 아니라 일부 비 기능적 언어에서도 표준 관행입니다. 예를 들어 Rust 참조 : doc.rust-lang.org/book/variable-bindings.html
Constantin

1
일부 환경에서는 현재 c ++ 표준도 있습니다. 예를 들어 람다 [x](){return ++x;}는 오류입니다. 여기
아나 톨릭

10
변수 "입니다 const기본적으로" :
피닉스

변수를 변경하기 위해 변수를 할당 할 필요는 없습니다. 그것들이 초기화되는 값은 런타임에 따라 달라질 수 있습니다.
Sphynx

80

다음 두 줄은 기능적으로 동일합니다.

int foo (int a);
int foo (const int a);

분명히 두 번째 방법으로 정의 된 경우 a본문에서 수정할 수 는 foo없지만 외부와의 차이는 없습니다.

어디 const정말 참조 또는 포인터를 매개 변수로 편리 온다 :

int foo (const BigStruct &a);
int foo (const BigStruct *a);

이것이 말하는 것은 foo는 복사하지 않고 큰 매개 변수, 아마도 기가 바이트 크기의 데이터 구조를 취할 수 있다는 것입니다. 또한 호출자에게 "Foo는 해당 매개 변수의 내용을 변경하지 않습니다."라고 말합니다. const 참조를 전달하면 컴파일러가 특정 성능 결정을 내릴 수도 있습니다.

* : 불변성을 없애지 않는 한, 그것은 또 다른 게시물입니다.


3
그것은이 질문에 관한 것이 아닙니다. 물론 참조되거나 지정된 인수의 경우 const를 사용하는 것이 좋습니다 (참조되거나 지정된 값이 수정되지 않은 경우). 포인터 예제에서 const 인 매개 변수 가 아닙니다 . 매개 변수가 가리키는 것입니다.
tml

> const 참조를 전달하면 컴파일러가 특정 성능 결정을 내릴 수도 있습니다. 고전적인 오류-컴파일러는 const-ness를 스스로 결정해야합니다. const 키워드는 포인터 앨리어싱과 const_cast 덕분에 도움이되지 않습니다
jheriko

73

여분의 불필요한 const는 API 관점에서 좋지 않습니다.

값에 의해 전달되는 내장형 매개 변수에 대해 불필요한 불필요한 const를 코드에 넣으면 API가 복잡해 지지만 호출자 또는 API 사용자에게 의미있는 약속은하지 않습니다 (구현을 방해 할뿐).

필요하지 않을 때 API에서 너무 많은 'const'는 " 울음 울음 소리 "와 같 으며, 결국 사람들은 'const'를 무시하기 시작합니다. 왜냐하면 그것이 어디에나 있고 대부분의 시간을 의미하지 않기 때문입니다.

API의 추가 const에 대한 "reductio ad absurdum"인수는 처음 두 가지 점에서 유용합니다. 더 많은 const 매개 변수가 좋으면 const를 가질 수있는 모든 인수는 const가 있어야합니다. 사실, 그것이 정말로 좋으면 const가 매개 변수의 기본값이되고 매개 변수를 변경하려는 경우에만 "mutable"과 같은 키워드를 원할 것입니다.

우리가 할 수있는 곳에 const를 입력 해 봅시다 :

void mungerum(char * buffer, const char * mask, int count);

void mungerum(char * const buffer, const char * const mask, const int count);

위의 코드 줄을 고려하십시오. 선언은 더 복잡하고 읽기가 길고 읽기 어려울뿐만 아니라 4 개의 'const'키워드 중 3 개를 API 사용자가 안전하게 무시할 수 있습니다. 그러나 'const'를 추가로 사용하면 두 번째 줄이 위험 할 수 있습니다!

왜?

첫 번째 매개 변수를 빨리 읽지 char * const buffer않으면 전달 된 데이터 버퍼의 메모리가 수정되지 않는다고 생각할 수 있지만 이는 사실이 아닙니다! 불필요한 'const'는 스캔하거나 빠르게 읽을 때 API에 대한 위험하고 잘못된 가정을 초래할 수 있습니다 .


불필요한 const는 코드 구현 관점에서도 좋지 않습니다.

#if FLEXIBLE_IMPLEMENTATION
       #define SUPERFLUOUS_CONST
#else
       #define SUPERFLUOUS_CONST             const
#endif

void bytecopy(char * SUPERFLUOUS_CONST dest,
   const char *source, SUPERFLUOUS_CONST int count);

FLEXIBLE_IMPLEMENTATION이 true가 아닌 경우, API는 아래 첫 번째 방법으로 함수를 구현하지 않을 것으로 "약속"합니다.

void bytecopy(char * SUPERFLUOUS_CONST dest,
   const char *source, SUPERFLUOUS_CONST int count)
{
       // Will break if !FLEXIBLE_IMPLEMENTATION
       while(count--)
       {
              *dest++=*source++;
       }
}

void bytecopy(char * SUPERFLUOUS_CONST dest,
   const char *source, SUPERFLUOUS_CONST int count)
{
       for(int i=0;i<count;i++)
       {
              dest[i]=source[i];
       }
}

그것은 매우 어리석은 약속입니다. 발신자에게 전혀 혜택을주지 않고 구현을 제한하는 약속을해야하는 이유는 무엇입니까?

두 가지 모두 동일한 기능을 완벽하게 유효하게 구현 한 것이므로 수행 한 모든 작업은 불필요하게 한 손으로 등 뒤로 묶여 있습니다.

게다가, 그것은 쉽게 (그리고 법적으로 우회되는) 매우 얕은 약속입니다.

inline void bytecopyWrapped(char * dest,
   const char *source, int count)
{
       while(count--)
       {
              *dest++=*source++;
       }
}
void bytecopy(char * SUPERFLUOUS_CONST dest,
   const char *source,SUPERFLUOUS_CONST int count)
{
    bytecopyWrapped(dest, source, count);
}

랩퍼 함수를 ​​사용하지 않겠다고 약속했지만 어쨌든 그렇게 구현했습니다. 나쁜 사람이 영화에서 누군가를 죽이지 않겠다고 약속하고 대신에 그를 대신 해달라고 명령하는 것과 같습니다.

그 불필요 한 const는 영화 나쁜 사람의 약속보다 더 가치가 없습니다.


그러나 거짓말하는 능력은 더욱 악화됩니다.

가짜 const를 사용하여 헤더 (선언)와 코드 (정의)의 const를 불일치 할 수 있음을 깨달았습니다. const-happy 옹호자들은 const를 정의에만 넣을 수 있기 때문에 이것이 좋은 것이라고 주장합니다.

// Example of const only in definition, not declaration
class foo { void test(int *pi); };
void foo::test(int * const pi) { }

그러나 그 반대는 사실입니다 ... 당신은 선언에만 가짜 const를 넣고 정의에서 무시할 수 있습니다. 이것은 API의 불필요한 const를 끔찍한 짓과 끔찍한 거짓말로 만듭니다.이 예제를보십시오.

class foo
{
    void test(int * const pi);
};

void foo::test(int *pi) // Look, the const in the definition is so superfluous I can ignore it here
{
    pi++;  // I promised in my definition I wouldn't modify this
}

불필요한 불필요한 const는 실제로 변수를 변경하거나 비 const 참조로 변수를 전달하려고 할 때 다른 로컬 사본 또는 래퍼 함수를 ​​사용하도록 강제함으로써 구현 자의 코드를 읽기 어렵게 만듭니다.

이 예를보십시오. 더 읽기 쉬운 것은 무엇입니까? 두 번째 함수의 추가 변수에 대한 유일한 이유는 일부 API 디자이너가 불필요한 const를 던 졌기 때문입니까?

struct llist
{
    llist * next;
};

void walkllist(llist *plist)
{
    llist *pnext;
    while(plist)
    {
        pnext=plist->next;
        walk(plist);
        plist=pnext;    // This line wouldn't compile if plist was const
    }
}

void walkllist(llist * SUPERFLUOUS_CONST plist)
{
    llist * pnotconst=plist;
    llist *pnext;
    while(pnotconst)
    {
        pnext=pnotconst->next;
        walk(pnotconst);
        pnotconst=pnext;
    }
}

잘만되면 우리는 여기에서 무언가를 배웠다. 불필요한 const는 API 혼란스러운 눈가리개, 성가신 잔소리, 얕고 의미없는 약속, 불필요한 방해이며 때로는 매우 위험한 실수로 이어집니다.


9
왜 다운 보트인가? downvote에 대한 간단한 의견을 남기면 훨씬 도움이됩니다.
Adisak

7
const 인수를 사용하는 요점은 표시된 행을 실패하게 만드는 것입니다 (plist = pnext). 함수 인수를 변경 불가능하게 유지하는 것은 합리적인 안전 조치입니다. 함수 선언이 잘못되었다는 점에 동의하지만 (Superflous이므로) 구현 블록에서 목적을 달성 할 수 있습니다.
touko

23
@Adisak 나는 당신의 대답 자체에 아무런 문제가 보이지 않지만, 당신의 의견에서 중요한 요점이 빠져있는 것 같습니다. 함수 정의 / 구현은 API의 일부 가 아니며 함수 선언 일뿐 입니다. 앞서 말했듯이 const 매개 변수로 함수를 선언하는 것은 의미가 없으며 혼란을 더합니다. 그러나 API 사용자는 구현을 볼 필요가 없습니다. 한편 구현자는 명확성을 위해 함수 정의의 일부 매개 변수 만 한정하기로 결정했을 수 있습니다.
jw013

17
@ jw013은 정확 void foo(int)하고 void foo(const int)오버로드가 아닌 정확히 동일한 기능입니다. ideone.com/npN4W4 ideone.com/tZav9R 여기의 const는 함수 본문의 구현 세부 사항 일 뿐이며 과부하 해결에는 영향을 미치지 않습니다. 보다 안전하고 깔끔한 API를 위해 const를 선언에서 제외 하고 복사 된 값을 수정하지 않으 려면 const를 정의 에 넣습니다 .
Oktalist

3
@Adisak 나는 이것이 오래되었다는 것을 알고 있지만 공개 API의 올바른 사용법은 다른 방법 일 것이라고 생각합니다. 그렇게하면 내부 작업을하는 개발자는 의도하지 않은 시간과 같은 실수를하지 않습니다 pi++.
CoffeeandCode

39

const는 C ++에서 기본값이었습니다. 이처럼 :

int i = 5 ; // i is a constant

var int i = 5 ; // i is a real variable

8
정확히 내 생각.
Constantin

24
적어도 C ++를 디자인하는 사람들에게는 이것을 고려하기에는 C와의 호환성이 너무 중요합니다.

4
흥미 롭습니다.
Dan

6
마찬가지로 unsignedC ++의 기본값이어야합니다. 이처럼 int i = 5; // i is unsigned하고 signed int i = 5; // i is signed.
hkBattousai

25

생계를 위해 C ++을 코딩 할 때 가능한 모든 것을 구성했습니다. const를 사용하면 컴파일러가 도움을 줄 수있는 좋은 방법입니다. 예를 들어, 메소드 리턴 값을 구성하면 다음과 같은 오타를 피할 수 있습니다.

foo() = 42

당신이 의미 할 때 :

foo() == 42

상수가 아닌 참조를 반환하도록 foo ()가 정의 된 경우 :

int& foo() { /* ... */ }

컴파일러는 행복하게 함수 호출에 의해 반환되는 익명의 임시 값을 지정할 수 있습니다. 그것을 const 만들기 :

const int& foo() { /* ... */ }

이 가능성을 제거합니다.


6
어떤 컴파일러로 작동합니까? 컴파일하는 동안 GCC는 오류를 제공 foo() = 42오류 : 좌변이 대입의 왼쪽 피연산자로 요구
gavrie

이것은 잘못된 것입니다. foo () = 42는 2 = 3과 같습니다. 즉 컴파일러 오류입니다. 그리고 const를 반환하는 것은 완전히 의미가 없습니다. 내장 유형에 대해서는 아무것도하지 않습니다.
Josh

2
나는 const의 사용법을 만났고 결국에는 이점보다 더 번거 롭습니다. 힌트 : const int foo()와 유형이 다르기 때문에 int foo()함수 포인터, 신호 / 슬롯 시스템 또는 부스트 :: 바인드와 같은 것을 사용하는 경우 큰 문제가 발생합니다.
Mephane

2
참조 반환 값을 포함하도록 코드를 수정했습니다.
Avdi

반환 값 최적화로 인해과 const int& foo()동일 하지 int foo()않습니까?
잔 티에

15

comp.lang.c ++. moderated에있는 오래된 "금주의 주"기사에서이 주제에 대한 좋은 토론이 있습니다 .

해당 GOTW 기사는 Herb Sutter의 웹 사이트 ( 여기)에 있습니다 .


1
Herb Sutter는 정말 똑똑한 사람입니다.
Adisak

2
좋은 기사이지만 논쟁에 대해서는 그와 동의하지 않습니다. 나는 그것들이 변수와 같기 때문에 그것들을 const로 만들고, 나는 아무도 내 주장을 변경하기를 원하지 않습니다.
QBziZ

9

const 매개 변수 값을 말합니다.

이 버기 기능을 고려하십시오.

bool isZero(int number)
{
  if (number = 0)  // whoops, should be number == 0
    return true;
  else
    return false;
}

만약 number 매개 변수가 const라면, 컴파일러는 버그를 멈추고 경고 할 것입니다.


2
다른 방법은 if ​​(0 == number) ... else ...;
Johannes Schaub-litb

5
@ ChrisHuang-Leaver 끔찍한 일이 아니라 요다처럼 말하면 : stackoverflow.com/a/2430307/210916
MPelletier

GCC / Clang -Wall은 -Wparentheses를 제공하며, 실제로 원하는 경우 "if ((number = 0))"이되도록 요구합니다. 요다 대신에 잘 작동합니다.
Jetski S-type

8

데이터 만 있고 함수에 의해 수정되지 않는 참조 (또는 포인터) 인 함수 매개 변수에 const를 사용합니다. 참조를 사용하는 목적이 데이터 복사를 피하고 전달 된 매개 변수를 변경할 수 없도록하는 것입니다.

예제에서 boolean b 매개 변수에 const를 넣으면 구현에 제약이 생겨 클래스의 인터페이스에 영향을 미치지 않습니다 (매개 변수를 변경하지 않는 것이 좋습니다).

에 대한 함수 서명

void foo(int a);

void foo(const int a);

.c와 .h를 설명하는 것과 같습니다.

아사 프


6

->*또는 .*연산자 를 사용하는 경우 필수입니다.

그것은 당신이 같은 것을 쓰지 못하게합니다.

void foo(Bar *p) { if (++p->*member > 0) { ... } }

내가 지금 거의 했었고 아마 당신이 의도 한 것을하지 않을 것입니다.

내가 말하려는 것은

void foo(Bar *p) { if (++(p->*member) > 0) { ... } }

나는을 넣어 한 경우 const사이 Bar *p, 컴파일러는 나에게 그 말 것이다.


4
나는 이미 많은 연산자를 혼합하려고 할 때 (우선 100 %를 모르는 경우) 연산자 우선 순위에 대한 참조를 즉시 확인하므로 IMO는 중요하지 않습니다.
mk12

5

아 힘든 일이야 한편으로 선언은 계약이며 const 인수를 값으로 전달하는 것은 실제로 의미가 없습니다. 반면에 함수 구현을 살펴보면 인수 상수를 선언하면 컴파일러에서 최적화 할 수있는 기회가 더 많아집니다.


5

호출자의 객체를 수정하지 않으므로 인수가 값으로 전달되면 const는 의미가 없습니다.

함수의 목적이 전달 된 값을 수정하는 것이 아니라면 참조로 전달할 때 const가 선호되어야합니다.

마지막으로 현재 객체 (this)를 수정하지 않는 함수는 const로 선언 될 수 있으며 아마도 선언되어야합니다. 예는 다음과 같습니다.

int SomeClass::GetValue() const {return m_internalValue;}

이것은이 호출이 적용되는 객체를 수정하지 않겠다는 약속입니다. 다시 말해 다음과 같이 전화 할 수 있습니다.

const SomeClass* pSomeClass;
pSomeClass->GetValue();

함수가 const가 아닌 경우 컴파일러 경고가 발생합니다.


5

값 매개 변수 'const'를 표시하는 것은 분명 주관적인 것입니다.

그러나 실제로는 예제에서와 같이 값 매개 변수를 const로 표시하는 것을 선호합니다.

void func(const int n, const long l) { /* ... */ }

나에게 값은 함수에 의해 함수 매개 변수 값이 변경되지 않는다는 것을 분명히 나타냅니다. 처음과 끝에서 같은 값을 갖습니다. 나에게 그것은 매우 기능적인 프로그래밍 스타일을 유지하는 것의 일부입니다.

짧은 함수의 경우 논쟁이 함수에 의해 수정되지 않는다는 것이 명백하기 때문에 일반적으로 거기에 'const'를 갖는 것은 시간 / 공간을 낭비하는 것입니다.

그러나 더 큰 함수의 경우 구현 문서 형식이며 컴파일러에서 적용합니다.

'n'과 'l'을 사용하여 계산을 수행하면 확신 할 수 있습니다. 하나 또는 둘 다 변경 된 장소를 놓 쳤기 때문에 다른 결과를 얻을 염려없이 계산을 리팩터링 / 이동할 수 있습니다.

구현 세부 사항이므로 구현에서 사용하는 것과 동일한 이름으로 함수 매개 변수를 선언 할 필요가없는 것처럼 헤더에 값 매개 변수 const를 선언 할 필요가 없습니다.


4

유효한 인수가 아닐 수도 있습니다. 그러나 함수 컴파일러 내에서 const 변수의 값을 증가 시키면 " error : incremental-read-only parameter " 오류가 발생 합니다. 즉, const 키워드를 함수 내에서 실수로 변수를 수정하는 것을 방지하는 방법으로 const 키워드를 사용할 수 있음을 의미합니다 (우리는 / 읽기 전용이 아닙니다). 우연히 컴파일 타임에 컴파일하면 컴파일러에서 알려줍니다. 이 프로젝트를 수행하는 유일한 사람이 아닌 경우 특히 중요합니다.


3

가능한 한 const를 사용하는 경향이 있습니다. (또는 대상 언어에 대한 다른 적절한 키워드입니다.) 컴파일러가 달리 할 수없는 추가 최적화를 수행 할 수 있기 때문에 순수 하게이 작업을 수행합니다. 이러한 최적화가 무엇인지 모릅니다. 어리석은 것처럼 보일지라도 항상 그렇게합니다.

내가 아는 한, 컴파일러는 const 값 매개 변수를 매우 잘 볼 수 있습니다. "이 함수는 어쨌든 수정하지 않으므로 참조로 전달하고 일부 클럭 사이클을 저장할 수 있습니다." 함수 서명을 변경하기 때문에 그런 일을 할 것이라고 생각하지 않지만 요점을 지적합니다. 어쩌면 그것은 다른 스택 조작이나 무언가를 할 수도 있습니다 ... 요점은, 몰라요,하지만 컴파일러가 똑똑해지면 부끄러워하는 것보다 똑똑하다는 것을 알고 있습니다.

C ++에는 const-correctness라는 아이디어와 함께 추가 수하물이 있으므로 더욱 중요합니다.


경우에 따라 도움이 될 수 있지만 최적화를 촉진 할 가능성은의 이점으로 과장되어 있다고 생각합니다 const. 오히려 그것은 구현 내에서 의도를 진술하고 나중에 사고를 잡는 문제입니다 (실수로 잘못된 로컬 변수를 증가시키지 않았습니다 const). 동시에, 함수가 인라인 될 수 있고 작업이 변경 될 수있는 전체 방식으로 인라인되면 컴파일러는 함수 시그니처를 변경하는 것을 매우 환영합니다. 참조 추가 또는 제거, '변수'리터럴 등은 모두 as-if 규칙 내에 있습니다.
underscore_d

3

1. 내 평가에 따른 최상의 답변 :

@Adisak의 답변 은 내 평가에 근거한 가장 좋은 답변입니다. 참고 그것은 또한 있기 때문에이 답변이 가장 중요한 부분에 있음을 가장 잘 백업 실제 코드 예제로 , 소리와 면밀한 논리를 사용하여뿐만 아니라.

2. 내 자신의 말 (가장 좋은 대답에 동의) :

  1. 값별 전달의 경우을 추가해도 이점이 없습니다 const. 그것이하는 모든 것 :
    1. 구현자가 소스 코드에서 입력 매개 변수를 변경할 때마다 사본을 작성하도록 제한하십시오 (전달 된 값이 값을 전달하므로 이미 사본이므로 변경 사항은 부작용이 없습니다). 그리고 종종 값으로 전달되는 입력 매개 변수를 변경하는 것이 함수를 구현하는 데 사용되므로 const모든 곳을 추가하면 이를 방해 할 수 있습니다.
    2. const불필요하게 추가하면 const어디서나 코드가 복잡해 지므로 const안전한 코드를 만드는 데 실제로 필요한 코드 에서주의를 끌 수 있습니다 .
  2. 다룰 때 포인터 또는 참조 하지만, const필요한 때 중요하다 해야 사용이 지속적으로 변화 원하지 않는 부작용을 방지로서, 외부 기능하므로 매 포인터 또는 참조 해야 사용 const위해서 PARM는 입력 만있는 경우, 출력이 아닙니다. 참조 또는 포인터에 의해 전달 된 매개 변수 const 에만 사용하면 어떤 매개 변수가 포인터 또는 참조 인지 분명하게 알 수 있습니다. "주의하십시오! const옆에있는 모든 매개 변수 는 참조 또는 포인터입니다!" 라고 말하는 것이 한 가지 더 있습니다.
  3. 위에서 설명한 것은 내가 일한 전문 소프트웨어 조직에서 얻은 합의에 대한 것이 었으며 모범 사례로 간주되었습니다. 때로는 규칙이 엄격했습니다. "값으로 전달되는 매개 변수에는 const를 사용하지 말고 입력 또는 입력으로 만 참조되는 경우 항상 참조 나 포인터로 전달 된 매개 변수에는 const를 사용하십시오."

3. Google의 말 (저와 동의하고 최선의 답변) :

( " Google C ++ 스타일 가이드 "에서)

값으로 전달 된 함수 매개 변수의 경우 const는 호출자에게 영향을 미치지 않으므로 함수 선언에서는 권장되지 않습니다. TotW # 109를 참조하십시오 .

지역 변수에 const를 사용하는 것은 권장하거나 권장하지 않습니다.

출처 : 구글 C ++ 스타일 가이드의 "사용 형 구조"섹션 : https://google.github.io/styleguide/cppguide.html#Use_of_const . 이것은 실제로 매우 유용한 섹션이므로 전체 섹션을 읽으십시오.

"TotW # 109"는 "주 # 109의 팁 : const기능 선언의 의미 "를 나타내며 유용한 참고 자료입니다 . 그것은 더 많은 정보와 무엇을해야하는지에 덜 규정 및 상황에 온에 기반 하기 전에 에서 Google C ++ 스타일 가이드 규칙 const바로 위에 인용하지만 제공되는 명확성의 결과, const바로 위에 인용 규칙은 구글 C에 추가 된 ++ 스타일 가이드.

또한 내 위치를 지키기 위해 여기에 Google C ++ 스타일 가이드를 인용하더라도 항상 가이드를 따르거나 가이드를 따르는 것이 좋습니다. 그들이 추천하는 것들 중 일부는 같은 이상한 그냥 평범한 있습니다 자신의 kDaysInAWeek"상수 이름"에 대한 스타일의 명명 규칙 . 그럼에도 불구하고, 세계에서 가장 성공하고 영향력있는 기술 및 소프트웨어 회사 중 하나가이 문제에 대한 우리의 견해를 뒷받침하기 위해 @ 나 Disak과 같은 정당성을 사용하는 시점을 지적하는 것이 여전히 유용하고 관련성이 있습니다.

4. Clang의 linter clang-tidy에는이를위한 몇 가지 옵션이 있습니다.

A. 그것은 연타의 린터가 지적 가치도있어 clang-tidy, 옵션이있다 readability-avoid-const-params-in-decls, 여기에 기술 지원, 코드베이스에 적용 되지 사용 const에 의한 전달 값 기능 매개 변수는 :

함수 선언에 최상위 const 인 매개 변수가 있는지 확인합니다.

선언의 const 값은 함수의 서명에 영향을 미치지 않으므로 거기에 두지 않아야합니다.

예 :

void f(const string);   // Bad: const is top level.
void f(const string&);  // Good: const is not top level.

그리고 완전성과 명확성을 위해 내가 추가하고있는 두 가지 예가 더 있습니다.

void f(char * const c_string);   // Bad: const is top level. [This makes the _pointer itself_, NOT what it points to, const]
void f(const char * c_string);   // Good: const is not top level. [This makes what is being _pointed to_ const]

B. 또한이 옵션이 있습니다 : readability-const-return-type- https://clang.llvm.org/extra/clang-tidy/checks/readability-const-return-type.html

5. 문제에 대한 스타일 가이드를 작성하는 방법에 대한 실용적인 접근 방식 :

간단히 이것을 복사하여 스타일 가이드에 붙여 넣습니다.

[복사 / 페이스트 시작]

  1. const내용이 가리키는 내용을 변경하지 않으려는 경우 항상 참조 또는 포인터로 전달 된 함수 매개 변수에 사용하십시오 . 이런 식으로, 참조 또는 포인터에 의해 전달 된 변수가 부족할 것으로 예상 될 때 분명해집니다 const. 이 사용 사례 const에서 기능 외부의 우발적 인 부작용을 방지합니다.
  2. 이되어 바람직하지 사용 const하기 때문에 함수 값에 의해 전달 된 파라미터에 const호출자에 대한 효과가 없음 : 변수 함수로 변화 시켜도 함수 외부 부작용이 없어야한다. 추가적인 칭의와 통찰력에 대해서는 다음 자료를 참조하십시오.
    1. "Google C ++ 스타일 가이드" "const 사용"섹션
    2. "주말 팁 # 109 : const기능 선언의 의미 "
    3. "함수 매개 변수에 'const'사용"에 대한 Adisak의 스택 오버 플로우 답변
  3. " 결코 사용 최상위는 const[즉 : const매개 변수 값에 의해 전달 ]에서 함수의 매개 변수에 정의되지 않습니다 선언 (및 / 의미없는 붙여 넣기 복사하지 않도록주의 const.) 그것은 의미가 컴파일러에 의해 무시, 그것은 시각적 노이즈 , 독자를 오도 할 수 있습니다 "( https://abseil.io/tips/109 , 강조 추가)
    1. 유일한 const컴파일에 영향을 규정은 그와 같은 헤더 파일 함수 () 메소드 선언 같이 NOT 그 함수의 전방 선언, 함수 정의에 배치된다.
  4. 사용하지 않을 최상위 const[즉, const변수의 값에 의해 전달 된 값에] 반환 하는 함수로한다.
  5. const함수가 반환 한 포인터 나 참조를 사용 하는 것은 때때로 유용 하기 때문에 구현 자에게 달려 있습니다.
  6. TODO : 다음 clang-tidy옵션 을 사용하여 위의 일부를 시행하십시오 .
    1. https://clang.llvm.org/extra/clang-tidy/checks/readability-avoid-const-params-in-decls.html
    2. https://clang.llvm.org/extra/clang-tidy/checks/readability-const-return-type.html

const위에서 설명한 규칙 을 보여주는 코드 예제는 다음과 같습니다 .

const매개 변수 예 :
( 여기서 빌린 )

void f(const std::string);   // Bad: const is top level.
void f(const std::string&);  // Good: const is not top level.

void f(char * const c_string);   // Bad: const is top level. [This makes the _pointer itself_, NOT what it points to, const]
void f(const char * c_string);   // Good: const is not top level. [This makes what is being _pointed to_ const]

const반환 유형 예 :
( 여기 에서 빌린 )

// BAD--do not do this:
const int foo();
const Clazz foo();
Clazz *const foo();

// OK--up to the implementer:
const int* foo();
const int& foo();
const Clazz* foo();

[복사 / 페이스트 종료]


2

언급 한 경우 API 호출자에게 영향을 미치지 않으므로 일반적으로 수행되지 않고 헤더에 필요하지 않습니다. 함수 구현에만 영향을 미칩니다.

특히 나쁜 일은 아니지만 API에 영향을 미치지 않고 타이핑을 추가하므로 일반적으로 수행되지 않는 이점이 그리 크지 않습니다.


2

값 전달 매개 변수에 const를 사용하지 않습니다. 호출자는 매개 변수 수정 여부를 신경 쓰지 않으며 구현 세부 사항입니다.

실제로 중요한 것은 메소드가 인스턴스를 수정하지 않으면 const로 표시하는 것입니다. 그렇지 않으면 const_cast <>가 많거나 const로 표시해야하는 다른 메소드를 호출하므로 메소드 const를 표시하려면 많은 코드를 변경해야한다는 것을 알 수 있기 때문에이 작업을 수행하십시오.

또한 로컬 변수를 수정할 필요가 없으면 const를 표시하는 경향이 있습니다. "움직이는 부분"을보다 쉽게 ​​식별함으로써 코드를 이해하기 쉽게 만듭니다.



2

나는 const 할 수 있었다. 매개 변수에 대한 상수는 값을 변경해서는 안됨을 의미합니다. 이것은 참조로 전달할 때 특히 유용합니다. 함수에 대한 const는 함수가 클래스 멤버를 변경하지 않아야 함을 선언합니다.


2

요약:

  • "일반적으로 const-by-value 값은 유용하지 않으며 잘못 오도합니다." 에서 GOTW006
  • 그러나 변수와 마찬가지로 .cpp에 추가 할 수 있습니다.
  • 표준 라이브러리는 const를 사용하지 않습니다. 예 std::vector::at(size_type pos). 표준 라이브러리에 충분한 것이 나에게 좋습니다.

2
"표준 라이브러리에 충분한 것이 나에게 좋은 것"이 항상 사실은 아닙니다. 예를 들어 표준 라이브러리는 _Tmp항상 못생긴 변수 이름 을 사용합니다. 원하지 않는 경우 (실제로는 사용할 수 없습니다).
아나 톨릭

1
@anatolyg 이것은 구현 세부 사항입니다
Fernando Pelliccioni

2
인수 목록의 변수 이름과 const 한정 유형은 모두 구현 세부 사항입니다. 내가 말하고 싶은 것은 표준 라이브러리의 구현이 때로는 좋지 않다는 것입니다. 때로는 더 잘할 수 있습니다. 10 년 전 표준 라이브러리 코드는 언제 작성 되었습니까? 5 년 전 (최신 부분)? 오늘 더 나은 코드를 작성할 수 있습니다.
아나 톨릭

1

매개 변수가 값으로 전달되고 참조가 아닌 경우 일반적으로 매개 변수가 const로 선언되는지 여부에 관계없이 참조 변수가 포함되어 있지 않은 한 내장 형식의 문제는 아닙니다. 매개 변수가 참조 또는 포인터 인 경우 일반적으로 포인터 자체가 아닌 참조 / 지적 된 메모리를 보호하는 것이 좋습니다 (참조를 변경할 수없는 것처럼 중요하지는 않지만 참조 자체를 const로 만들 수는 없다고 생각합니다) . const로 가능한 모든 것을 보호하는 것이 좋습니다. 매개 변수가 POD (내장 유형 포함) 일 뿐이고 도로를 따라 더 이상 변경 될 가능성이없는 경우 (예 : 부울 매개 변수) 실수를하지 않아도됩니다.

.h / .cpp 파일 선언 차이에 대해서는 몰랐지만 의미가 있습니다. 머신 코드 수준에서는 "const"가 없으므로 .h에서 함수를 비 const로 선언하면 코드는 const로 선언하는 것과 같습니다 (최적화 제외). 그러나 함수 구현 (.ccp) 내에서 변수 값을 변경하지 않도록 컴파일러를 참여시키는 데 도움이됩니다. 변경을 허용하는 인터페이스에서 상속하는 경우에 유용하지만 필요한 기능을 달성하기 위해 매개 변수로 변경할 필요는 없습니다.


0

나는 그런 매개 변수에 const를 넣지 않을 것입니다-모두 부울 (boolean &와 반대)이 일정하다는 것을 알고 있으므로 그것을 추가하면 사람들이 "대기, 무엇?" 또는 참조로 매개 변수를 전달한다고해도.


4
때로는 성능상의 이유로 참조로 객체를 전달하고 싶지만 변경하지 않으려는 경우 const가 필수입니다. 그런 모든 매개 변수-bools-const를 유지하면 코드를 쉽게 읽을 수 있도록하는 것이 좋습니다.
gbjbaanb

0

const로 기억해야 할 것은 나중에 시도하고 배치하는 것보다 처음부터 const를 만드는 것이 훨씬 쉽다는 것입니다.

변경되지 않은 것을 원할 때 const를 사용하십시오-함수의 기능과 기대하는 것을 설명하는 추가 힌트. 나는 그들 중 일부와 특히 C 문자열을 허용하는 C API를 보았습니다!

헤더보다 cpp 파일에서 const 키워드를 생략하는 경향이 있지만, 잘라내어 붙여 넣는 경향이 있으므로 두 위치 모두에 보관됩니다. 컴파일러가 왜 그것을 허용하는지 모르겠습니다. 컴파일러가 생각합니다. 가장 좋은 방법은 const 키워드를 두 파일에 모두 넣는 것입니다.


나는 이것을 전혀 얻지 못한다. cpp 파일 (함수 정의)에서 생략하겠습니까? 그것은 실제로 무언가를 의미하고 오류를 잡을 수있는 곳입니다. const를 두 곳에 두는 것이 가장 좋은 방법이라고 생각하는 이유무엇 입니까? 헤더 파일 (함수 선언)에서 그것은 아무것도 의미하지 않으며 API를 복잡하게 만듭니다. 어쩌면 decl과 defn을 똑같이 보이게하는 데 약간의 가치가있을 수도 있지만 API를 어지럽히는 문제와 비교할 때 실제로 작은 이점 인 것 같습니다.
Don Hatch

@DonHatch 8 년 후 와우. 어쨌든 OP가 말했듯이 함수 선언에서 매개 변수에서 const를 생략 할 수 있지만 함수 정의에 포함시킬 수 있다는 사실에 놀랐습니다.
gbjbaanb

0

함수가 변수의 사본 만 수정할 수 있기 때문에 값 매개 변수를 "const"로 만들 이유가 없습니다.

"const"를 사용하는 이유는 참조로 더 큰 무언가 (예 : 멤버가 많은 구조체)를 전달하는 경우 함수가이를 수정할 수 없도록하기 때문입니다. 또는 일반적인 방식으로 수정하려고하면 컴파일러에서 불만을 표시합니다. 실수로 수정되는 것을 방지합니다.


0

Const 매개 변수는 매개 변수가 참조, 즉 참조 또는 포인터로 전달 될 때만 유용합니다. 컴파일러가 const 매개 변수를 볼 때 매개 변수에 사용 된 변수가 함수 본문 내에서 수정되지 않았는지 확인합니다. 왜 누군가가 값으로 매개 변수를 상수로 만들고 싶습니까? :-)


많은 이유들로 인해. 값별 매개 변수를 const명확하게 지정하면 '이것을 수정할 필요가 없으므로 선언합니다. 나중에 수정하려고하면 실수를 수정하거나로 표시를 해제 할 수 있도록 컴파일 타임 오류를 알려주십시오 const. ' 따라서 코드 위생과 안전 문제입니다. 구현 파일에 추가하는 데 필요한 모든 것은 사람들이 순수한 반사, IMO로하는 일이어야합니다.
underscore_d

0

매개 변수가 값으로 전달되므로 호출 함수의 관점에서 const를 지정하거나 지정하지 않으면 아무런 차이가 없습니다. 기본적으로 값 매개 변수로 전달을 const로 선언하는 것은 의미가 없습니다.


0

예제의 모든 const는 목적이 없습니다. C ++은 기본적으로 값으로 전달되므로 함수는 이러한 int 및 boolean의 사본을 가져옵니다. 함수가이를 수정하더라도 호출자의 사본에는 영향을 미치지 않습니다.

그래서 여분의 const를 피할 것입니다.

  • 그들은 redudant입니다
  • 그들은 텍스트를 어수선하게
  • 유용하거나 효율적인 경우 전달 된 값을 변경하지 못하게합니다.

-1

나는 그 질문이 "약간"구식이라는 것을 알고 있지만, 내가 다른 사람도 나중에 그렇게 할 수도 있다는 것을 안다. ... 아직도 가난한 사람이 내 의견을 읽기 위해 여기에 적어 놓을 지 의심된다. ​​:)

우리는 여전히 C 스타일 사고 방식에 너무 국한되어있는 것 같습니다. OOP 패러 다마에서 우리는 유형이 아닌 객체를 가지고 놀았습니다. Const 객체는 개념적으로 비 Const 객체와 개념적으로 다를 수 있으며, 특히 논리적 Const 의미 (비트 단위 Const)와 다릅니다. 따라서 함수 매개 변수의 const 정확성이 POD의 경우 (아마도) 과도하게주의하더라도 객체의 경우에는 그렇지 않습니다. 함수가 const 객체와 함께 작동하면 그렇게 말해야합니다. 다음 코드 스 니펫을 고려하십시오.

#include <iostream>

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
class SharedBuffer {
private:

  int fakeData;

  int const & Get_(int i) const
  {

    std::cout << "Accessing buffer element" << std::endl;
    return fakeData;

  }

public:

  int & operator[](int i)
  {

    Unique();
    return const_cast<int &>(Get_(i));

  }

  int const & operator[](int i) const
  {

    return Get_(i);

  }

  void Unique()
  {

    std::cout << "Making buffer unique (expensive operation)" << std::endl;

  }

};

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void NonConstF(SharedBuffer x)
{

  x[0] = 1;

}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void ConstF(const SharedBuffer x)
{

  int q = x[0];

}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int main()
{

  SharedBuffer x;

  NonConstF(x);

  std::cout << std::endl;

  ConstF(x);

  return 0;

}

추신 : (const) 참조가 여기에 더 적절할 것이라고 주장 할 수 있으며 동일한 행동을 제공합니다. 글쎄요 내가 다른 곳에서 볼 수있는 것과 다른 그림을주는 것 ...

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