This revision ofN1952reflects additional clarifications and wording changes from the CWG; thereare no substantive changes to the proposal.
This revision ofN1855has improved wording for a few sections based on the invaluable advice of theCWG obtained at the Mont Tremblant meeting.
The specific revisions toN1855are:
This document slightly revisesN1770to fix a problem found in the use of rvalue stream objects.
Consider:
#include <ostream>class A{public: A(int i) : i_(i) {} int get() const {return i_;}private: int i_;};std::ostream&operator<< (std::ostream& os, const A& a){ return os << a.get();}This is ill formed code according toN1770:
return os << a.get();Error: {lval}ostream << {rval}int is ambiguous:std::ostream::operator<< (int); // #1operator<<(std::ostream&, const A&); // #2The first overload is a member function ofostream and thus has thefollowingpseudo-signature:
operator<<(std::ostream::operator&&, int); // #1
To match #1, the first parameteros is converted from an lvalue torvalue to match the implicit object parameter and the second parameter is anexact match.
To match #2, the first parameter is an exact match, but the second parameter isconverted via the A(int) constructor.
According to 13.3.3p1, the call is ambiguous because #1 favors the secondimplicit conversion sequence while #2 favors the first implicit conversionsequence.
We believe the source of the trouble is thatN1770changed the type of the implicit object parameter from an lvalue-reference to anrvalue-reference (which was motivated by eliminating one exception to thegeneral rules of the language).
This revision corrects that mistake by reverting the type of the implicit objectparameter to an lvalue-reference (as it is in C++03), and adding a clarificationto the special treatment that the implicit object parameter already receivesduring overload resolution: An rvalue to lvalue conversion to match the implicitobject parameter does not affect overload resolution (which is also true inC++03).
The specific revisions toN1770are:
The purpose of this document is to provide proposed wording for thervaluereference proposal documented byN1690andN1377.
The proposed wording herein avoids defining the termsmove constructorandmove assignment in order to avoid possible conflict or confusion withsimilarterms which we wish to introduce into the library.N1771introducesMoveConstructible andMoveAssignable requirements,defining these concepts in terms of expressions and post-conditions, instead ofin terms of constructors or function signatures.
-1- Compound types can be constructed in the following ways:
-5- The result of calling a function that does not returna referencean lvalue-reference is an rvalue. User defined operators arefunctions, and whether such operators expect or yield lvalues is determined bytheir parameter and return types.
-6- An expression which holds a temporary object resulting from a cast to anonreference typetype other than an lvalue-reference typeis an rvalue (this includes the explicit creation of an object using functionalnotation (5.2.3)).
-3- An expressione can beimplicitly converted to a typeT if and only if the declaration ``T t=e;'' is well-formed,for some invented temporary variablet (dcl.init). The effect of theimplicit conversion is the same as performing the declaration and initializationand then using the temporary variable as the result of the conversion. Theresult is an lvalue ifT isa referenceanlvalue-reference type (dcl.ref), and an rvalue otherwise. The expressione is used as an lvalue if and only if the initialization uses it as anlvalue.
-6- If an expression initially has the type ``lvalue-reference toT''(dcl.ref, dcl.init.ref), the type is adjusted to ``T'' prior to anyfurther analysis, the expression designates the objector function denoted by thelvalue-reference, and the expression is an lvalue.
-7- If an expression initially has the type ``rvalue-reference toT''(dcl.ref, dcl.init.ref), the type is adjusted to ``T'' prior to anyfurther analysis, and the expression designates the objector function denoted by the rvalue-reference. If the expression is the result ofcalling a function, whether implicitly or explicitly, it is an rvalue;otherwise, it is an lvalue. [Note: In general, the effect of thisrule is that named rvalue references are treated as lvalues and unnamedrvalue references are treated as rvalues. —end note]
[Example:
struct A {};A&& operator+(A,A);A&& f();...A a;A&& ar = a;The expressionsf() anda + a are rvalues of typeA. The expressionar is an lvalue of typeA.
--end example]
-10- A function call is an lvalue if and only if the result type isareferencean lvalue-reference.
-2- IfT is a pointer type,v shall be an rvalue of a pointerto complete class type, and the result is an rvalue of typeT. IfT isa referencean lvalue-reference type,v shall be an lvalue of a complete class type, and the result is anlvalue of the type referred to byT.IfT is anrvalue-reference type,v shall be an expression having a complete classtype, and the result is an rvalue of the type referred to byT.
-5- IfT is ``pointer tocv1 B'' andv hastype ``pointer tocv2 D'' such thatB is a base classofD, the result is a pointer to the uniqueB sub-object oftheD object pointed to byv. Similarly, ifT is``reference tocv1 B'' andv has type ``cv2D'' such thatB is a base class ofD, the result isan lvalue for the uniqueB sub-object of theDobject referred to byv.The result is an lvalue ifT isan lvalue-reference, or an rvalue ifT is an rvalue-reference.
...
-1- The result of the expressionstatic_cast<T>(v) is the resultof converting the expressionv to typeT. IfT isa referencean lvalue-reference type, the result is anlvalue; otherwise, the result is an rvalue. Types shall not be defined in astatic_cast. Thestatic_cast operator shall not cast awayconstness (expr.const.cast).
-2- An lvalue of type ``cv1 B'', whereB is a classtype, can be cast to type ``reference tocv2 D'', whereD is a class derived (clause class.derived) fromB, if a validstandard conversion from ``pointer toD'' to ``pointer toB''exists (conv.ptr),cv2 is the same cv-qualification as, orgreater cv-qualification than,cv1, andB is not avirtual base class ofD.The resultis an lvalue ofhas type``cv2 D.'' and is an lvalue ifthe type cast to is anlvalue-reference; otherwise the result is an rvalue.An rvalue of type``cv1 B'' can be cast to type ``rvalue-reference tocv2 D'' with the same constraints as noted for the lvalue oftype ``cv1 B,'' resulting in an rvalue. If thelvalueobject of type ``cv1 B'' isactually a sub-object of an object of typeD, thelvalueresult refers to the enclosing object of typeD. Otherwise,the result of the cast is undefined.
...
-3- Otherwise, an expressione can be explicitly converted to a typeT using astatic_cast of the formstatic_cast<T>(e) if the declaration ``T t(e);'' iswell-formed, for some invented temporary variablet (dcl.init). Theeffect of such an explicit conversion is the same as performing the declarationand initialization and then using the temporary variable as the result of theconversion. The result is an lvalue ifT isa referencean lvalue-reference type (dcl.ref), and an rvalue otherwise. Theexpressione is used as an lvalue if and only if the initializationuses it as an lvalue.
-1- The result of the expressionreinterpret_cast<T>(v) is theresult of converting the expressionv to typeT. IfTisa referencean lvalue-reference type, the result is anlvalue; otherwise, the result is an rvalue and the lvalue-to-rvalue (conv.lval),array-to-pointer (conv.array), and function-to-pointer (conv.func) standardconversions are performed on the the expressionv. Types shall not bedefined in areinterpret_cast. Conversions that can be performedexplicitly usingreinterpret_cast are listed below. No other conversioncan be performed explicitly usingreinterpret_cast.
-11- An lvalue expression of typeT1 can be cast to the type``reference toT2'' if an expression of type ``pointer toT1''can be explicitly converted to the type ``pointer toT2'' using areinterpret_cast. That is, a reference castreinterpret_cast<T&>(x) has the same effect as the conversion*reinterpret_cast<T*>(&x) with the built-in& and* operators(and similarly forreinterpret_cast<T&&>(x)). The resultis anlvalue that refers to the sameobject as the source lvalue, but with a different type.The result is anlvalue for lvalue-references or an rvalue for rvalue-references. Notemporary is created, no copy is made, and constructors (class.ctor) orconversion functions (class.conv) are not called.
-1- The result of the expressionconst_cast<T>(v) is of typeT. IfT isa referenceanlvalue-reference type, the result is an lvalue; otherwise, the result isan rvalue and, the lvalue-to-rvalue (conv.lval), array-to-pointer (conv.array),and function-to-pointer (conv.func) standard conversions are performed on theexpressionv. Types shall not be defined in aconst_cast.Conversions that can be performed explicitly usingconst_cast arelisted below. No other conversion shall be performed explicitly usingconst_cast.
-4- An lvalue of typeT1 can be explicitly converted to an lvalue oftypeT2 using the castconst_cast<T2&> (whereT1 andT2 are object types) if a pointer toT1 can beexplicitly converted to the type pointer toT2 using a const_cast.Similarly, an expression of typeT1 can be explicitly converted toan rvalue of typeT2 using the castconst_cast<T2&&>(whereT1 andT2 are object types) if a pointer toT1can be explicitly converted to the type pointer toT2 using aconst_cast. The result of a referenceconst_cast refersto the original object.
-1- The result of the expression(T)cast-expression is of typeT. The result is an lvalue ifT isa referencean lvalue-reference type, otherwise the result is an rvalue. [Note:ifT is a non-class type that is cv-qualified, the cv-qualifiers areignored when determining the type of the resulting rvalue; see basic.lval. ]
-5- The conversions performed by
can be performed using the cast notation of explicit type conversion. The samesemantic restrictions and behaviors apply, with the exception that in performingastatic_cast in the following situations the conversion is valid evenif the base class is inaccessible:
...
-3- Otherwise, if the second and third operand have different types ...
-2- A return statement without an expression can be used only in functions thatdo not return a value, that is, a function with the return typevoid, aconstructor (class.ctor), or a destructor (class.dtor). A return statement withan expression of non-void type can be used only in functions returning a value;the value of the expression is returned to the caller of the function. Theexpression is implicitly converted to the return type of the function in whichit appears. A return statement can involve the construction and copy of atemporary object (class.temporary).[Note:A copy operation associated with areturn statement may be elided or considered as an rvalue for the purpose ofoverload resolution in selecting a constructor (12.8).—end note]Flowing off the end of a function isequivalent to areturn with no value; this results in undefinedbehavior in a value-returning function.
-9- If a typedefTD names a type``reference tocv1 S''that is a reference to a typeT,an attempt to create the type``reference tocv2 TD''``lvalue-reference tocv TD'' creates thetype``reference tocv12 S'', wherecv12 is theunion of the cv-qualifierscv1 andcv2. Redundant qualifiers are ignored``lvalue-reference toT'',while an attempt to create the type ``rvalue-reference tocv TD''creates the typeTD.
[Example:
int i;typedef int & RI;RI & r1 = i; / / r1 has the type int&const RI & r2 = i; / / r2 has the type const int&int i;typedef int& LRI;typedef int&& RRI;LRI& r1 = i; // r1 has the type int&const LRI& r2 = i; // r2 has the type int&const LRI&& r3 = i; // r3 has the type int&RRI& r4 = i; // r4 has the type int&RRI&& r5 = i; // r5 has the type int&&
--end example]
-4- Declarators have the syntax
declarator: direct-declarator ptr-operator declaratordirect-declarator: declarator-id direct-declarator ( parameter-declaration-clause ) cv-qualifier-seqopt exception-specificationopt direct-declarator [ constant-expressionopt ] ( declarator )ptr-operator: * cv-qualifier-seqopt &&& ::opt nested-name-specifier * cv-qualifier-seqoptcv-qualifier-seq: cv-qualifier cv-qualifier-seqopt...
-1- In a declarationT D whereD haseither ofthe forms
& D1&& D1
and the type of the identifier in the declarationT D1 is``derived-declarator-type-listT,'' then the type of theidentifier ofD is``derived-declarator-type-list reference toT.'' Cv-qualified references are ill-formed except when thecv-qualifiers are introduced through the use of a typedef (dcl.typedef) or of atemplate type argument (temp.arg), in which case the cv-qualifiers are ignored.[Example: in
typedef int& A;const A aref = 3; // ill-formed; // non-const reference initialized with rvalue
the type ofaref is ``reference toint'', not ``constreference to int''.] [Note: a reference can be thought of as a name of anobject. ] A declarator that specifies the type ``reference to cv void'' isill-formed.
A reference type that is declared using& is called anlvalue-reference, and a reference type that is declared using&& iscalled anrvalue-reference. Lvalue-references and rvalue-references are distincttypes. Except where explicitly noted, they are semantically equivalent andcommonly referred to as references.
Note: The changes proposed for core issue 391 are reflected in the "old" textbelow.
-1- A variable declared to be aT&orT&&, that is``reference to typeT'' (dcl.ref), shall be initialized by an object,or function, of typeT or by an object that can be converted into aT.
-5- A reference to type ``cv1 T1'' is initialized by anexpression of type ``cv2 T2'' as follows:
[Footnote: This requires a conversion function (class.conv.fct) returning areference type. --- end foonote](this conversion is selected byenumerating the applicable conversion functions (over.match.ref) and choosingthe best one through overload resolution (over.match)),
double d = 2.0;double& rd = d; // rdrefers to dconst double& rcd = d; // rcdrefers to dstruct A { };struct B : public A { } b;A& ra = b; // rarefers to Asub-object in bconst A& rca = b; // rcarefers to Asub-object in b--end example]--end example]double& rd2 = 2.0; //error: not an lvalue and reference not constint i = 2;double& rd3 = i; //error: type mismatch and reference not constdouble&& rd4 = i; //OK, reference bound to temporary double
struct A { };struct B : public A { } b;extern B f();const A& rca = f(); //Bound to theA sub-object of theB rvalueA&& rcb = f(); //Same as above--end example]--end example]const double& rcd2 = 2; // rcd2refers to temporary with value 2.0double&& rcd3 = 2; // rcd3refers to temporary with value 2.0const volatile int cvi = 1;const int& r = cvi; //error: type qualifiers dropped
-2- A non-template constructor for classX is acopy constructorif its first parameter is of typeX&,const X&,volatileX& orconst volatile X&, and either there are no other parametersor else all other parameters have default arguments (dcl.fct.default).*
[Footnote: Because a template constructoror a constructor whose firstparameter is an rvalue-reference is never a copy constructor, thepresence of such a
templateconstructor does not suppressthe implicit declaration of a copy constructor.TemplateSuch constructors participate in overloadresolution with other constructors, including copy constructors, and, ifselected, willa templateconstructor maybe used to copy anobject if it provides a better match than other constructors. --- endfoonote]
[Example:X::X(const X&) andX::X(X&, int=1) are copyconstructors.
class X { // ...public: X(int); X(const X&, int = 1);};X a(1); // calls X(int);X b(a, 0); // calls X(const X&, int);X c = b; // calls X(const X&, int);--- end example] [Note: all forms of copy constructor may be declared fora class. [Example:
class X { // ...public: X(const X&); X(X&); // OK};--- end example]
--- end note] [Note: if a classX only hasa copy constructor with a parameter of typeX&, an initializer of typeconst X orvolatile X cannot initialize an object of type(possibily cv-qualified)X. [Example:
struct X { X(); // default constructor X(X&); // copy constructor with a nonconst parameter};const X cx;X x = cx; // error - X::X(X&) cannot copy cx into x--- end example]
--- end note]
-9- A user-declaredcopy assignment operatorX::operator= is anon-static non-template member function of classX with exactly oneparameter of typeX,X&,const X&,volatileX& orconst volatile X&.*
[Footnote: Because a template assignment operatoror an assignment operatortaking an rvalue reference parameter is never a copy assignment operator,the presence of such
a templatean assignment operatordoes not suppress the implicit declaration of a copy assignment operator.TemplateSuch assignment operators participate in overloadresolution with other assignment operators, including copy assignment operators,and, if selected willa template assignment operator maybe used to assign an objectif it provides a better match than otherassignment operators. --- end foonote]
[Note: an overloaded assignment operator must be declared to have onlyone parameter; see over.ass. ] [Note: more than one form of copyassignment operator may be declared for a class. ] [Note: if a classX only has a copy assignment operator with a parameter of typeX&, an expression of typeconst X cannot be assigned to anobject of typeX. [Example:
struct X { X(); X& operator=(X&);};const X cx;X x;void f() { x = cx; // error: // X::operator=(X&) cannot assign cx into x}--- end example]
--- end note]
-15- When certain criteria are met ...
-16- When the criteria for elision of a copy operation are met and theobject to be copied is designated by an lvalue, overload resolution toselect the constructor for the copy is first performed as if the objectwere designated by an rvalue. If overload resolution fails, or if thetype of the first parameter of the selected constructor is not anrvalue-reference to the object's type (possibly cv-qualified),overload resolution is performed again, considering the object asan lvalue. [Note: This two-stage overload resolution must beperformed regardless of whether copy elision will occur. It determinesthe constructor to be called if elision is not performed, and theselected constructor must be accessible even if the call is elided.—end note]
[Example:
class Thing {public: Thing(); ~Thing(); Thing(Thing&&);private: Thing(const Thing&);};Thing f(bool b) { Thing t; if (b) throw t; //OK, Thing(Thing&&) used (or elided) to throw t return t; //OK, Thing(Thing&&) used (or elided) to return t}Thing t2 = f(); //OK, Thing(Thing&&) used (or elided) for construction of t2--- end example]
-5- During overload resolution, the implied object argument is indistinguishablefrom other arguments. The implicit object parameter, however, retains itsidentity since conversions on the corresponding argument shall obey theseadditional rules:
-1- Under the conditions specified in dcl.init, as part of a copy-initializationof an object of class type, a user-defined conversion can be invoked to convertan initializer expression to the type of the object being initialized. Overloadresolution is used to select the user-defined conversion to be invoked. Assumingthat ``cv1 T'' is the type of the object being initialized, withT a class type, the candidate functions are selected as follows:
-1- Under the conditions specified in dcl.init, as part of an initialization ofan object of nonclass type, a conversion function can be invoked to convert aninitializer expression of class type to the type of the object beinginitialized. Overload resolution is used to select the conversion function to beinvoked. Assuming that ``cv1 T'' is the type of the object beinginitialized, and ``cv S'' is the type of the initializerexpression, withS a class type, the candidate functions are selectedas follows:
-1- Under the conditions specified in dcl.init.ref, a reference can be bounddirectly to an lvalue that is the result of applying a conversion function to aninitializer expression. Overload resolution is used to select the conversionfunction to be invoked. Assuming that ``cv1T'' is theunderlying type of the reference being initialized, and ``cvS''is the type of the initializer expression, withS a class type, thecandidate functions are selected as follows:
-3- A standard conversion sequence cannot be formed if it requires bindinga referencean lvalue-reference to non-const to an rvalue(except when binding an implicit object parameter; see the special rules forthat case in over.match.funcs). [Note: this means, for example, that acandidate function cannot be a viable function if it has a non-constlvalue-reference parameter (other than the implicit object parameter)and the corresponding argument is a temporary or would require one to be createdto initialize thelvalue-reference (see dcl.init.ref). ]
-4- Other restrictions on binding a reference to a particular argument do notaffect the formation of a standard conversion sequence, however.[Example: a function with a ``lvalue-reference toint'' parameter can be a viable candidate even if the correspondingargument is anint bit-field. The formation of implicit conversionsequences treats theint bit-field as anint lvalue and findsan exact match with the parameter. If the function is selected by overloadresolution, the call will nonetheless be ill-formed because of the prohibitionon binding a non-constlvalue-reference to a bit-field(dcl.init.ref). ]
-3- Two implicit conversion sequences of the same form are indistinguishableconversion sequences unless one of the following rules apply:
...
--- end example] or, if not that,int f(const int *);int f(int *);int i;int j = f(&i); // Calls f(int *)
int i;int f();int g(const int &);int g(const int &&);int j = g(i); // Calls g(const int &)int k = g(f()); // Calls g(const int &&)struct A {A& operator<<(int);};A& operator<<(A&&, char);A() << 1; // Calls A::operator<<(int)A() << 'c'; // Calls operator<<(A&&, char)A a;a << 1; // Calls A::operator<<(int)a << 'c'; // Calls operator<<(A&&, char)---end example] or, if not that,-4- If atemplate-argument for atemplate-parameterTnames a type``reference tocv1 S''that is areference to a typeA, an attempt tocreate the type``reference tocv2 T''``lvalue-reference tocv T'' creates the type``reference tocv12 S'', wherecv12 is theunion of the cv-qualifierscv1 andcv2. Redundantcv-qualifiers are ignored``lvalue-reference toA'', whilean attempt to create the type ``rvalue-reference tocv T''creates the typeT. [Example:
template <class T> class X { f(const T&);g(T&&); /* ... */};X<int&> x1; // X<int&>::f has the parameter typeconst int& // X<int&>::g has the parameter type int&X<const int&&> x2; // X<const int&&>::f has the parameter type const int& // X<const int&&>::g has the parameter type const int&&--end example]
-3- IfP is a cv-qualified type, the top level cv-qualifiers ofP's type are ignored for type deduction. IfP is a referencetype, the type referred to byP is used for type deduction.IfP is an rvalue-reference type and the argument is an lvalue,the typeA& is used in place ofA for type deduction.[Example:
template<typename T> int f(T&&);int i;int j = f(i); // calls f<int&>(i)
---end example] [Note: The effect of this rule for lvaluearguments and rvalue-reference parameters is that deduction in such caseswill fail unless the function parameter is of the formcv T&& (14.8.2.5). —end note]
-8- A template type argumentT, a template template argumentTTor a template non-type argumenti can be deduced ifP andA have one of the following forms:
Tcv-list TT*T&T&&T[integer-constant]...
Many thanks to William M. (Mike) Miller, Edison Design Group, Inc, for theexcellent review and suggestions.