Namespaces
Variants

Reference declaration

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

기존에 존재하는 객체나 함수에 대한 별칭인 참조(reference)로 명명된 변수를 선언합니다.

목차

구문

참조 변수 선언은 선언자 가 다음과 같은 형태를 갖는 모든 단순 선언입니다

& attr  (선택 사항) declarator (1)
&& attr  (선택 사항) declarator (2) (since C++11)
1) Lvalue reference declarator : 선언문 S & D ; D decl-specifier-seq S 에 의해 결정된 타입에 대한 lvalue reference 로 선언합니다.
2) Rvalue reference declarator : 선언문 S && D ; D decl-specifier-seq S 에 의해 결정된 타입에 대한 rvalue reference 로 선언합니다.
declarator - 참조 선언자, 배열 선언자 , 그리고 포인터 선언자 를 제외한 모든 선언자 (참조에 대한 참조, 참조의 배열, 참조에 대한 포인터는 존재하지 않음)
attr - (C++11부터) 속성 목록

참조는 유효한 객체나 함수를 참조하도록 초기화되어야 합니다: reference initialization 을 참조하십시오.

"void(에 cv 한정자가 있을 수 있음)에 대한 참조" 유형은 형성될 수 없습니다.

참조 타입은 최상위 수준에서 cv-qualified 될 수 없습니다; 선언에서 이를 위한 문법이 존재하지 않으며, typedef-name 또는 decltype 지정자, (C++11부터) 또는 type template parameter 에 한정자가 추가되는 경우 무시됩니다.

참조는 객체가 아닙니다; 참조는 반드시 저장 공간을 차지하지는 않지만, 컴파일러가 원하는 의미론을 구현하기 위해 필요하다면 저장 공간을 할당할 수 있습니다 (예: 참조 타입의 비정적 데이터 멤버는 일반적으로 메모리 주소를 저장하는 데 필요한 크기만큼 클래스의 크기를 증가시킵니다).

참조는 객체가 아니기 때문에, 참조의 배열, 참조에 대한 포인터, 참조에 대한 참조는 존재하지 않습니다:

int& a[3]; // 오류
int&* p;   // 오류
int& &r;   // 오류

참조 축약

템플릿이나 typedef에서 타입 조작을 통해 참조에 대한 참조를 형성하는 것이 허용되며, 이 경우 참조 축약 규칙이 적용됩니다: rvalue 참조에 대한 rvalue 참조는 rvalue 참조로 축약되며, 다른 모든 조합은 lvalue 참조를 형성합니다:

typedef int&  lref;
typedef int&& rref;
int n;
lref&  r1 = n; // type of r1 is int&
lref&& r2 = n; // type of r2 is int&
rref&  r3 = n; // type of r3 is int&
rref&& r4 = 1; // type of r4 is int&&

(이는 함수 템플릿에서 T&& 가 사용될 때의 템플릿 인수 추론 에 대한 특별 규칙과 함께, std::forward 를 가능하게 하는 규칙들을 형성합니다.)

(C++11부터)

Lvalue 참조

Lvalue 참조는 기존 객체의 별칭으로 사용될 수 있습니다 (선택적으로 다른 cv-qualification과 함께):

#include <iostream>
#include <string>
int main()
{
    std::string s = "Ex";
    std::string& r1 = s;
    const std::string& r2 = s;
    r1 += "ample";           // s를 수정함
//  r2 += "!";               // 오류: const 참조를 통해 수정할 수 없음
    std::cout << r2 << '\n'; // s를 출력하며, 현재 "Example"을 보유함
}

이들은 함수 호출에서 참조에 의한 전달(pass-by-reference) 의미를 구현하는 데에도 사용될 수 있습니다:

#include <iostream>
#include <string>
void double_string(std::string& s)
{
    s += s; // 's'는 main()의 'str'과 같은 객체입니다
}
int main()
{
    std::string str = "Test";
    double_string(str);
    std::cout << str << '\n';
}

함수의 반환 타입이 lvalue 참조일 때, 함수 호출 표현식은 lvalue 표현식이 됩니다:

#include <iostream>
#include <string>
char& char_number(std::string& s, std::size_t n)
{
    return s.at(n); // string::at()은 char에 대한 참조를 반환합니다
}
int main()
{
    std::string str = "Test";
    char_number(str, 1) = 'a'; // 함수 호출은 lvalue이므로 할당 가능합니다
    std::cout << str << '\n';
}

Rvalue 참조

Rvalue 참조는 임시 객체의 수명을 연장 하는 데 사용될 수 있습니다 (참고: const에 대한 lvalue 참조도 임시 객체의 수명을 연장할 수 있지만, 이를 통해 수정할 수는 없습니다):

#include <iostream>
#include <string>
int main()
{
    std::string s1 = "Test";
//  std::string&& r1 = s1;           // 오류: lvalue에 바인딩할 수 없음
    const std::string& r2 = s1 + s1; // 정상: const에 대한 lvalue 참조가 수명을 연장함
//  r2 += "Test";                    // 오류: const 참조를 통해 수정할 수 없음
    std::string&& r3 = s1 + s1;      // 정상: rvalue 참조가 수명을 연장함
    r3 += "Test";                    // 정상: non-const 참조를 통해 수정할 수 있음
    std::cout << r3 << '\n';
}

더 중요하게는, 함수가 rvalue 참조와 lvalue 참조 오버로드 를 모두 가질 때, rvalue 참조 오버로드는 rvalue(prvalue와 xvalue 모두 포함)에 바인딩되고, lvalue 참조 오버로드는 lvalue에 바인딩됩니다:

#include <iostream>
#include <utility>
void f(int& x)
{
    std::cout << "lvalue reference overload f(" << x << ")\n";
}
void f(const int& x)
{
    std::cout << "lvalue reference to const overload f(" << x << ")\n";
}
void f(int&& x)
{
    std::cout << "rvalue reference overload f(" << x << ")\n";
}
int main()
{
    int i = 1;
    const int ci = 2;
    f(i);  // f(int&) 호출
    f(ci); // f(const int&) 호출
    f(3);  // f(int&&) 호출
           // f(int&&) 오버로드가 제공되지 않았다면 f(const int&)를 호출함
    f(std::move(i)); // f(int&&) 호출
    // rvalue reference 변수는 표현식에서 사용될 때 lvalue임
    int&& x = 1;
    f(x);            // f(int& x) 호출
    f(std::move(x)); // f(int&& x) 호출
}

이는 적합한 경우 move constructors , move assignment 연산자, 그리고 다른 move-aware 함수들(예: std::vector::push_back() )이 자동으로 선택되도록 합니다.

rvalue 참조는 xvalue에 바인딩될 수 있으므로 비임시 객체를 참조할 수 있습니다:

int i2 = 42;
int&& rri = std::move(i2); // i2에 직접 바인딩됩니다

이렇게 하면 더 이상 필요하지 않은 범위 내 객체에서 이동이 가능해집니다:

std::vector<int> v{1, 2, 3, 4, 5};
std::vector<int> v2(std::move(v)); // v에 대한 rvalue 참조를 바인딩합니다
assert(v.empty());

전달 참조

포워딩 참조(Forwarding references)는 함수 인자의 값 범주(value category)를 보존하는 특별한 종류의 참조로, 전달(forward) std::forward 를 통해 가능하게 합니다. 포워딩 참조는 다음 중 하나입니다:

1) 동일한 함수 템플릿의 cv-unqualified type template parameter 에 대한 rvalue reference로 선언된 함수 템플릿의 함수 매개변수:
template<class T>
int f(T&& x)                      // x는 전달 참조(forwarding reference)입니다
{
    return g(std::forward<T>(x)); // 따라서 전달(forward)될 수 있습니다
}
int main()
{
    int i;
    f(i); // 인수가 lvalue, f<int&>(int&) 호출, std::forward<int&>(x)는 lvalue
    f(0); // 인수가 rvalue, f<int>(int&&) 호출, std::forward<int>(x)는 rvalue
}
template<class T>
int g(const T&& x); // x는 전달 참조가 아닙니다: const T는 cv-unqualified가 아닙니다
template<class T>
struct A
{
    template<class U>
    A(T&& x, U&& y, int* p); // x는 전달 참조가 아닙니다: T는 생성자의
                             // 타입 템플릿 매개변수가 아니지만,
                             // y는 전달 참조입니다
};
2) auto && 중괄호로 둘러싸인 초기화 목록에서 추론되는 경우를 제외하고 또는 클래스 템플릿의 템플릿 매개변수를 나타낼 때 클래스 템플릿 인수 추론 중인 경우 (C++17부터) :
auto&& vec = foo();       // foo()는 lvalue 또는 rvalue일 수 있으며, vec은 전달 참조(forwarding reference)입니다
auto i = std::begin(vec); // 두 경우 모두 동작합니다
(*i)++;                   // 두 경우 모두 동작합니다
g(std::forward<decltype(vec)>(vec)); // 값 범주(value category)를 보존하며 전달합니다
for (auto&& x: f())
{
    // x는 전달 참조입니다; 이는 제네릭 코드에서 범위 기반 for 루프를 사용하는 일반적인 방법입니다
}
auto&& z = {1, 2, 3}; // 전달 참조가 *아닙니다* (초기화 리스트에 대한 특별한 경우)

참고 항목 template argument deduction std::forward .

(C++11 이후)

댕글링 참조

참조는 초기화 시 항상 유효한 객체나 함수를 가리키지만, 참조된 객체의 수명 이 끝났음에도 참조가 접근 가능한 상태로 남아 있는 ( dangling ) 프로그램을 만들 수 있습니다.

참조 타입의 표현식 expr 이 주어지고, 해당 참조가 나타내는 객체나 함수를 target 이라고 할 때:

  • 만약 target 에 대한 포인터가 expr 의 평가 맥락에서 유효하다면 , 결과는 target 을 지정합니다.
  • 그렇지 않으면, 동작은 정의되지 않습니다.
std::string& f()
{
    std::string s = "Example";
    return s; // s의 범위를 벗어남:
              // 소멸자가 호출되고 저장 공간이 해제됨
}
std::string& r = f(); // 댕글링 참조
std::cout << r;       // 정의되지 않은 동작: 댕글링 참조에서 읽기
std::string s = f();  // 정의되지 않은 동작: 댕글링 참조로부터 복사 초기화

rvalue 참조와 const에 대한 lvalue 참조는 임시 객체의 수명을 연장합니다(규칙과 예외 사항은 Reference initialization 참조).

참조된 객체가 소멸되었지만(예: 명시적 소멸자 호출), 저장 공간이 해제되지 않은 경우, 수명이 끝난 객체에 대한 참조는 제한된 방식으로 사용될 수 있으며, 동일한 저장 공간에 객체가 재생성되면 유효해질 수 있습니다(자세한 내용은 Access outside of lifetime 참조).

타입 접근 불가 참조

변환된 초기화식이 lvalue (until C++11) glvalue (since C++11) 이고, 이를 통해 객체가 type-accessible 하지 않은 경우, 해당 객체에 대한 참조를 바인딩하려는 시도는 정의되지 않은 동작을 초래합니다:

char x alignas(int);
int& ir = *reinterpret_cast<int*>(&x); // 정의되지 않은 동작:
                                       // 초기화자가 char 객체를 참조함

호환되지 않는 호출 참조

변환된 초기화식이 lvalue인 (C++11까지) glvalue인 (C++11부터) 함수에 대한 참조를 바인딩하려고 시도할 때, 해당 타입이 함수 정의의 타입과 호환 가능(call-compatible) 하지 않으면 정의되지 않은 동작이 발생합니다:

void f(int);
using F = void(float);
F& ir = *reinterpret_cast<F*>(&f); // 정의되지 않은 동작:
                                   // 초기화자가 void(int) 함수를 참조함

참고 사항

기능 테스트 매크로 표준 기능
__cpp_rvalue_references 200610L (C++11) Rvalue references

결함 보고서

다음의 동작 변경 결함 보고서들은 이전에 발표된 C++ 표준에 소급 적용되었습니다.

DR 적용 대상 게시된 동작 올바른 동작
CWG 453 C++98 참조를 어떤 객체나 함수에 바인딩할 수 없는지 불분명했음 명확히 규정됨
CWG 1510 C++11 decltype 피연산자에서 cv-한정된 참조를 형성할 수 없었음 허용됨
CWG 2550 C++98 매개변수가 " void 에 대한 참조" 타입을 가질 수 있었음 허용되지 않음
CWG 2933 C++98 댕글링 참조 접근 동작이 불분명했음 명확히 규정됨

외부 링크

Thomas Becker, 2013 - C++ Rvalue References Explained