code

int 대 const int &

codestyles 2020. 12. 28. 08:10
반응형

int 대 const int &


나는 일반적으로 상수 참조를 반환 값이나 인수로 사용한다는 것을 알았습니다. 그 이유는 코드에서 비 참조를 사용하는 것과 거의 동일하게 작동하기 때문이라고 생각합니다. 그러나 분명히 더 많은 공간이 필요하고 함수 선언이 길어집니다. 나는 그러한 코드에 대해 괜찮지 만 어떤 사람들은 그것이 나쁜 프로그래밍 스타일이라고 생각합니다.

어떻게 생각해? const int & over int를 쓸 가치가 있습니까? 어쨌든 컴파일러에 의해 최적화되었다고 생각하므로 코딩 시간을 낭비하고있을 수 있습니다.


C ++에서는 매개 변수를 다룰 때 const T&똑똑한 방법으로 사용하는 안티 패턴이라고 생각하는 것이 매우 일반적 T입니다. 그러나 값과 참조 (const 여부에 관계없이)는 완전히 다른 두 가지이며 값 대신 참조를 항상 맹목적으로 사용하면 미묘한 버그가 발생할 수 있습니다.

그 이유는 참조를 다룰 때 값과 함께 존재하지 않는 두 가지 문제인 수명별칭을 고려해야하기 때문 입니다.

이 안티 패턴이 적용되는 한 곳은 표준 라이브러리 자체입니다. 여기서는 값 대신 std::vector<T>::push_back매개 변수 a const T&받아들이고 이것은 예를 들어 다음과 같은 코드에서 물릴 수 있습니다.

std::vector<T> v;
...
if (v.size())
    v.push_back(v[0]); // Add first element also as last element

이 코드는 std::vector::push_backconst 참조를 원하지만 push_back을 수행하려면 재 할당이 필요할 수 있으며 재 할당 후 수신 된 참조가 더 이상 유효하지 않다는 의미이며 ( 수명 문제) 정의되지 않은 동작 영역에 들어갑니다.

별칭 문제는 값 대신 const 참조가 사용되는 경우 미묘한 문제의 원인이기도합니다. 나는 예를 들어 이런 종류의 코드에 물렸다.

struct P2d
{ 
    double x, y;
    P2d(double x, double y) : x(x), y(y) {}
    P2d& operator+=(const P2d& p) { x+=p.x; y+=p.y; return *this; }
    P2d& operator-=(const P2d& p) { x-=p.x; y-=p.y; return *this; }
};

struct Rect
{
    P2d tl, br;
    Rect(const P2d& tl, const P2d& br) : tl(tl), bt(br) {}
    Rect& operator+=(const P2d& p) { tl+=p; br+=p; return *this; }
    Rect& operator-=(const P2d& p) { tl-=p; br-=p; return *this; }
};

코드는 언뜻보기에 매우 안전하고, 2 P2d차원 점 Rect이며, 직사각형이며 점을 더하거나 빼는 것은 직사각형을 변환하는 것을 의미합니다.

그러나 원점에서 사각형을 다시 myrect -= myrect.tl;번역하려면 번역 연산자가 (이 경우) 동일한 인스턴스의 멤버를 참조하는 참조를 허용하도록 정의 되었기 때문에 코드 를 작성 하면 작동하지 않습니다.

함께 좌상를 업데이트 한 후 것을이 수단 tl -= p;좌상이 될 것입니다 (0, 0)예상대로뿐만 아니라 p동시에 될 것입니다 (0, 0)때문에 p좌상 회원과 오른쪽 하단의 업데이트 작업은 번역 할 수 없기 때문에, 그래서 그냥 참조입니다 그것은에 의해 (0, 0)따라서 기본적으로 아무것도하지 않고.

const 참조가라는 단어 때문에 값과 같다고 속지 마십시오 const. 이 단어는 해당 reference를 사용하여 참조 된 객체를 변경하려고 할 때 컴파일 오류를 제공하기 위해서만 존재 하지만 참조 된 객체가 상수라는 것을 의미하지는 않습니다. 보다 구체적으로 const ref가 참조하는 객체는 변경 될 수 있으며 (예 : aliasing으로 인해 ) 사용하는 동안 존재하지 않을 수도 있습니다 ( 수명 문제).

에서 const T&단어 CONST 의 속성 표현 참조를 하지의, 참조 된 개체 :이 객체를 변경하는 데 사용하는 것은 불가능하게 재산입니다. 아마 읽기 전용 으로 더 좋은 이름을했을 CONST는 IMO 객체는 참조를 사용하는 동안 일정하게 될 것입니다한다는 생각을 밀어의 심리적 효과가 있습니다.

물론 값을 복사하는 대신 참조를 사용하면 특히 큰 클래스의 경우 인상적인 속도 향상을 얻을 수 있습니다. 그러나 참조를 사용할 때는 항상 별칭 및 수명 문제에 대해 생각해야합니다. 겉으로는 다른 데이터에 대한 포인터 일 뿐이 기 때문입니다. 그러나 "네이티브"데이터 유형 (int, double, 포인터)의 경우 참조는 실제로 값보다 느리며 값 대신 사용하여 얻을 수있는 것이 없습니다.

또한 const 참조는 컴파일러가 편집증이되어야하고 알 수없는 코드가 실행될 때마다 모든 참조 된 객체가 이제 다른 값을 가질 수 있다고 가정해야하기 때문에 항상 최적화 프로그램에 문제를 의미합니다 ( const참조의 경우 최적화 프로그램에 대해 절대 NOTHING을 의미합니다. ; 그 단어는 프로그래머를 돕기 위해서만 존재합니다. 개인적으로 그렇게 큰 도움이 될지는 모르겠지만 그것은 또 다른 이야기입니다).


Oli가 말했듯이 a const T&반환하는 T것은 완전히 다른 것이며 특정 상황에서 중단 될 수 있습니다 (그의 예에서와 같이).

촬영 const T&일반 반대로 T인수가 문제를 일으킬 가능성이 적습니다으로,하지만 여전히 몇 가지 중요한 차이점이 있습니다.

  • 촬영 T대신하는 것은 const T&그 필요 T복사 작도입니다.
  • take T는 비용이 많이들 수있는 복사 생성자를 호출합니다 (함수 종료시 소멸자).
  • 사진 촬영 T은 (빠른 수동으로 복사하는 것보다 될 수 있습니다) 로컬 변수로 매개 변수를 수정할 수 있습니다.
  • const T&잘못 정렬 된 임시 및 간접 비용으로 인해 복용 이 느려질 수 있습니다.

호출 수신자와 호출자가 별도의 컴파일 단위로 정의 된 경우 컴파일러는 참조를 최적화 할 수 없습니다. 예를 들어 다음 코드를 컴파일했습니다.

#include <ctime>
#include <iostream>

int test1(int i);
int test2(const int& i);

int main() {
  int i = std::time(0);
  int j = test1(i);
  int k = test2(i);
  std::cout << j + k << std::endl;
}

64 비트 Linux에서 최적화 수준 3의 G ++ 사용. 첫 번째 호출은 주 메모리에 대한 액세스가 필요하지 않습니다.

call    time
movl    %eax, %edi     #1
movl    %eax, 12(%rsp) #2
call    _Z5test1i
leaq    12(%rsp), %rdi #3
movl    %eax, %ebx
call    _Z5test2RKi

Line #1 directly uses the return value in eax as argument for test1 in edi. Line #2 and #3 push the result into main memory and place the address in the first argument because the argument is declared as reference to int, and so it must be possible to e.g. take its address. Whether something can be calculated entirely using registers or needs to access main memory can make a great difference these days. So, apart from being more to type, const int& can also be slower. The rule of thumb is, pass all data that is at most as large as the word size by value, and everything else by reference to const. Also pass templated arguments by reference to const; since the compiler has access to the definition of the template, it can always optimize the reference away.


int & and int are not interchangeable! In particular, if you return a reference to a local stack variable, the behaviour is undefined, e.g.:

int &func()
{
    int x = 42;
    return x;
}

You can return a reference to something that won't be destroyed at the end of the function (e.g. a static, or a class member). So this is valid:

int &func()
{
    static int x = 42;
    return x;
}

and to the outside world, has the same effect as returning the int directly (except that you can now modify it, which is why you see const int & a lot).

The advantage of the reference is that no copy is required, which is important if you're dealing with large class objects. However, in many cases, the compiler can optimize that away; see e.g. http://en.wikipedia.org/wiki/Return_value_optimization.


Instead of "thinking" it's optimized away by the compiler, why don't you get the assembler listing and find out for sure?

junk.c++:

int my_int()
{
    static int v = 5;
    return v;
}

const int& my_int_ref()
{
    static int v = 5;
    return v;
}

Generated assembler output (elided):

_Z6my_intv:
.LFB0:
    .cfi_startproc
    .cfi_personality 0x3,__gxx_personality_v0
    movl    $5, %eax
    ret
    .cfi_endproc

...

_Z10my_int_refv:
.LFB1:
    .cfi_startproc
    .cfi_personality 0x3,__gxx_personality_v0
    movl    $_ZZ10my_int_refvE1v, %eax
    ret

The movl instructions in both are very different. The first moves 5 into EAX (which happens to be the register traditionally used to return values in x86 C code) and the second moves the address of a variable (specifics elided for clarity) into EAX. That means the calling function in the first case can just directly use register operations without hitting memory to use the answer while in the second it has to hit memory through the returned pointer.

So it looks like it's not optimized away.

This is over and above the other answers you've been given here explaining why T and const T& are not interchangeable.


int is different with const int&:

  1. const int& is the reference to another integer variable (int B), which means: if we change int B, the value of const int& will also change.

2, int is the value copy of another integer variable (int B), which means: if we change int B, the value of int will not change.

See the following c++ code:

int main(){

vector a{1,2,3};

int b = a[2];//the value not change even when vector change

const int& c = a[2];//this is reference, so the value depend on vector;

a[2]=111;

// b will output 3;

// c will output 111;

}

ReferenceURL : https://stackoverflow.com/questions/4705593/int-vs-const-int

반응형