• Home   /  
  • Archive by category "1"

C++ Reference Variable Assignments

In the C++programming language, a reference is a simple reference datatype that is less powerful but safer than the pointer type inherited from C. The name C++ reference may cause confusion, as in computer science a reference is a general concept datatype, with pointers and C++ references being specific reference datatype implementations. The definition of a reference in C++ is such that it does not need to exist. It can be implemented as a new name for an existing object (similar to rename keyword in Ada).

Syntax and terminology[edit]

The declaration of the form:

<Type> & <Name>

where is a type and is an identifier whose type is reference to .

Examples:

Here, and are of type "reference to "

is a function that returns a "reference to "

is a function with a reference parameter, which is a "reference to "

is a with a member which is reference to

is a function that returns a (non-reference type) and is an alias for

is a constant reference pointing to a piece of storage having value 65.

Types which are of kind "reference to " are sometimes called reference types. Identifiers which are of reference type are called reference variables. To call them variable, however, is in fact a misnomer, as we will see.

Relationship to pointers[edit]

C++ references differ from pointers in several essential ways:

  • It is not possible to refer directly to a reference object after it is defined; any occurrence of its name refers directly to the object it references.
  • Once a reference is created, it cannot be later made to reference another object; it cannot be reseated. This is often done with pointers.
  • References cannot be null, whereas pointers can; every reference refers to some object, although it may or may not be valid. Note that for this reason, containers of references are not allowed.
  • References cannot be uninitialized. Because it is impossible to reinitialize a reference, they must be initialized as soon as they are created. In particular, local and global variables must be initialized where they are defined, and references which are data members of class instances must be initialized in the initializer list of the class's constructor. For example:
    int&k;// compiler will complain: error: `k' declared as reference but not initialized

There is a simple conversion between pointers and references: the address-of operator () will yield a pointer referring to the same object when applied to a reference, and a reference which is initialized from the dereference () of a pointer value will refer to the same object as that pointer, where this is possible without invoking undefined behavior. This equivalence is a reflection of the typical implementation, which effectively compiles references into pointers which are implicitly dereferenced at each use. Though that is usually the case, the C++ Standard does not force compilers to implement references using pointers.

A consequence of this is that in many implementations, operating on a variable with automatic or static lifetime through a reference, although syntactically similar to accessing it directly, can involve hidden dereference operations that are costly.

Also, because the operations on references are so limited, they are much easier to understand than pointers and are more resistant to errors. While pointers can be made invalid through a variety of mechanisms, ranging from carrying a null value to out-of-bounds arithmetic to illegal casts to producing them from arbitrary integers, a previously-valid reference only becomes invalid in two cases:

  • If it refers to an object with automatic allocation which goes out of scope,
  • If it refers to an object inside a block of dynamic memory which has been freed.

The first is easy to detect automatically if the reference has static scoping, but is still a problem if the reference is a member of a dynamically allocated object; the second is more difficult to assure. These are the only concerns with references, and are suitably addressed by a reasonable allocation policy.

Uses of references[edit]

  • Other than just a helpful replacement for pointers, one convenient application of references is in function parameter lists, where they allow passing of parameters used for output with no explicit address-taking by the caller. For example:
voidsquare(intx,int&result){result=x*x;}

Then, the following call would place 9 in y:

However, the following call would give a compiler error, since reference parameters not qualified with can only be bound to addressable values:

  • Returning a reference allows function calls to be assigned to:
    int&preinc(int&x){return++x;// "return x++;" would have been wrong}preinc(y)=5;// same as ++y, y = 5
  • In many implementations, normal parameter-passing mechanisms often imply an expensive copy operation for large parameters. References qualified with are a useful way of passing large objects between functions that avoids this overhead:
    voidf_slow(BigObjectx){/* ... */}voidf_fast(constBigObject&x){/* ... */}BigObjecty;f_slow(y);// slow, copies y to parameter xf_fast(y);// fast, gives direct read-only access to y

If actually requires its own copy of x that it can modify, it must create a copy explicitly. While the same technique could be applied using pointers, this would involve modifying every call site of the function to add cumbersome address-of () operators to the argument, and would be equally difficult to undo, if the object became smaller later on.

Polymorphic behavior[edit]

Continuing the relationship between references and pointers (in C++ context), the former exhibit polymorphic capabilities, as one might expect:

#include<iostream>usingnamespacestd;classA{public:A(){}virtualvoidprint(){cout<<"This is class A\n";}};classB:publicA{public:B(){}virtualvoidprint(){cout<<"This is class B\n";}};intmain(){Aa;A&refToa=a;Bb;A&refTob=b;refToa.print();refTob.print();return0;}

The source above is valid C++ and generates the following output:

ISO definition[edit]

References are defined by the ISO C++ standard as follows (excluding the example section):

In a declaration T D where D has the form

and the type of the identifier in the declaration T D1 is "derived-declarator-type-list," then the type of the identifier of D is "derived-declarator-type-list reference to ." Cv-qualified references are ill-formed except when the cv-qualifiers ( and volatile) are introduced through the use of a (7.1.3) or of a template type argument (14.3), in which case the cv-qualifiers are ignored. [Example: in

typedefint&A;constAaref=3;// ill-formed;// non-const reference initialized with rvalue

the type of is "reference to ", not " reference to ". ] [Note: a reference can be thought of as a name of an object. ] A declarator that specifies the type "reference to cv void" is ill-formed.

It is unspecified whether or not a reference requires storage (3.7).

There shall be no references to references, no arrays of references, and no pointers to references. The declaration of a reference shall contain an initializer (8.5.3) except when the declaration contains an explicit specifier (7.1.1), is a class member (9.2) declaration within a class declaration, or is the declaration of a parameter or a return type (8.3.5); see 3.1. A reference shall be initialized to refer to a valid object or function. [Note: in particular, a null reference cannot exist in a well-defined program, because the only way to create such a reference would be to bind it to the "object" obtained by dereferencing a null pointer, which causes undefined behavior. As described in 9.6, a reference cannot be bound directly to a bitfield. ]

— ISO/IEC 14882:1998(E), the ISO C++ standard, in section 8.3.2 [dcl.ref]

External links[edit]

References[edit]

Declares a named variable as a reference, that is, an alias to an already-existing object or function.

[edit]Syntax

A reference variable declaration is any simple declaration whose declarator has the form

attr(optional)declarator (1)
attr(optional)declarator (2) (since C++11)

1)Lvalue reference declarator: the declaration S& D; declares as an lvalue reference to the type determined by decl-specifier-seq.

2)Rvalue reference declarator: the declaration S&& D; declares as an rvalue reference to the type determined by decl-specifier-seq.

declarator - any declarator except another reference declarator (there are no references to references)
attr(C++11) - optional list of attributes

A reference is required to be initialized to refer to a valid object or function: see reference initialization.

There are no references to void and no references to references.

Reference types cannot be cv-qualified at the top level; there is no syntax for that in declaration, and if a qualification is introduced through a typedef, decltype, or template type argument, it is ignored.

References are not objects; they do not necessarily occupy storage, although the compiler may allocate storage if it is necessary to implement the desired semantics (e.g. a non-static data member of reference type usually increases the size of the class by the amount necessary to store a memory address).

Because references are not objects, there are no arrays of references, no pointers to references, and no references to references:

Reference collapsing

It is permitted to form references to references through type manipulations in templates or typedefs, in which case the reference collapsing rules apply: rvalue reference to rvalue reference collapses to rvalue reference, all other combinations form lvalue reference:

(This, along with special rules for template argument deduction when is used in a function template, forms the rules that make std::forward possible.)

typedefint& lref;typedefint&& 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&&
(since C++11)

[edit]Lvalue references

Lvalue references can be used to alias an existing object (optionally with different cv-qualification):

Run this code

#include <iostream>#include <string>   int main(){std::string s ="Ex";std::string& r1 = s;conststd::string& r2 = s;   r1 +="ample";// modifies s// r2 += "!"; // error: cannot modify through reference to conststd::cout<< r2 <<'\n';// prints s, which now holds "Example"}

They can also be used to implement pass-by-reference semantics in function calls:

Run this code

#include <iostream>#include <string>   void double_string(std::string& s){ s += s;// 's' is the same object as main()'s 'str'}   int main(){std::string str ="Test"; double_string(str);std::cout<< str <<'\n';}

When a function's return type is lvalue reference, the function call expression becomes an lvalue expression:

Run this code

#include <iostream>#include <string>   char& char_number(std::string& s, std::size_t n){return s.at(n);// string::at() returns a reference to char}   int main(){std::string str ="Test"; char_number(str, 1)='a';// the function call is lvalue, can be assigned tostd::cout<< str <<'\n';}

Rvalue references

Rvalue references can be used to extend the lifetimes of temporary objects (note, lvalue references to const can extend the lifetimes of temporary objects too, but they are not modifiable through them):

Run this code

#include <iostream>#include <string>   int main(){std::string s1 ="Test";// std::string&& r1 = s1; // error: can't bind to lvalue   conststd::string& r2 = s1 + s1;// okay: lvalue reference to const extends lifetime// r2 += "Test"; // error: can't modify through reference to const   std::string&& r3 = s1 + s1;// okay: rvalue reference extends lifetime r3 +="Test";// okay: can modify through reference to non-conststd::cout<< r3 <<'\n';}

More importantly, when a function has both rvalue reference and lvalue reference overloads, the rvalue reference overload binds to rvalues (including both prvalues and xvalues), while the lvalue reference overload binds to lvalues:

Run this code

#include <iostream>#include <utility>   void f(int& x){std::cout<<"lvalue reference overload f("<< x <<")\n";}   void f(constint& 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;constint ci =2; f(i);// calls f(int&) f(ci);// calls f(const int&) f(3);// calls f(int&&)// would call f(const int&) if f(int&&) overload wasn't provided f(std::move(i));// calls f(int&&)   // rvalue reference variables are lvalues when used in expressionsint&& x =1; f(x);// calls f(int& x) f(std::move(x));// calls f(int&& x)}

This allows move constructors, move assignment operators, and other move-aware functions (e.g. to be automatically selected when suitable.

Because rvalue references can bind to xvalues, they can refer to non-temporary objects

This makes it possible to move out of an object in scope that is no longer needed

int i2 =42;int&& rri = std::move(i2);// bound directly to i2
std::vector<int> v{1,2,3,4,5};std::vector<int> v2(std::move(v));// binds an rvalue reference to vassert(v.empty());
(since C++11)

Forwarding references

Forwarding references are a special kind of references that preserve the value category of a function argument, making it possible to forward it by means of std::forward. Forwarding references are either:

1) function parameter of a function template declared as rvalue reference to cv-unqualified type template parameter of that same function template:
template<class T>int f(T&& x){// x is a forwarding referencereturn g(std::forward<T>(x));// and so can be forwarded}int main(){int i; f(i);// argument is lvalue, calls f<int&>(int&), std::forward<T>(x) is lvalue f(0);// argument is rvalue: calls f<int>(int&&), std::forward<T>(x) is rvalue}   template<class T>int g(const T&& y);// y is not a forwarding reference: const T is not exactly T   template<class T>struct A {template<class U> A(T&& x, U&& y, int* p);// x is not a forwarding reference, but y is};
2)auto&& except when deduced from a brace-enclosed initializer list.
auto&& vec = foo();// foo() may be lvalue or rvalue, vec is a forwarding referenceauto i =std::begin(vec);// works either way(*i)++;// works either way g(std::forward<decltype(vec)>(vec));// forwards, preserving value category   for(auto&& x: f()){// x is a forwarding reference; this is the safest way to use range for loops}   auto&& z ={1,2,3};// *not* a forwarding reference (special case for initializer lists)

See also template argument deduction and std::forward

(since C++11)

[edit]Dangling references

Although references, once initialized, always refer to valid objects or functions, it is possible to create a program where the lifetime of the referred-to object ends, but the reference remains accessible (dangling). Accessing such a reference is undefined behavior. A common example is a function returning a reference to an automatic variable:

Note that rvalue references and lvalue references to const extend the lifetimes of temporary objects (see reference_initialization#Lifetime_of_a_temporary for rules and exceptions).

If the referred-to object was destroyed (e.g. by explicit destructor call), but the storage was not deallocated, a reference to the out-of-lifetime object may be used in limited ways, and may become valid if the object is recreated in the same storage (see Access outside of lifetime for details).

int& a[3];// errorint&* p;// errorint&&r;// error
std::string& f(){std::string s ="Example";return s;// exits the scope of s:// its destructor is called and its storage deallocated}   std::string& r = f();// dangling referencestd::cout<< r;// undefined behavior: reads from a dangling referencestd::string s = f();// undefined behavior: copy-initializes from a dangling reference

One thought on “C++ Reference Variable Assignments

Leave a comment

L'indirizzo email non verrà pubblicato. I campi obbligatori sono contrassegnati *