This rule is part of MISRA C++:2023.
Usage of this content is governed by Sonar’s terms and conditions. Redistribution is
prohibited.
Rule 6.8.4 - Member functions returning references to their object should be ref-qualified appropriately
[basic.life]
Category: Advisory
Analysis: Decidable,Single Translation Unit
Amplification
This rule applies to member functions with reference or pointer return type, where, in the definition of the function, at least one return
expression explicitly designates this, *this or a subobject of *this.
Such a function is only appropriately ref-qualified when:
- It is non-const-lvalue-ref-qualified (
&); or
- It is const-lvalue-ref-qualified (
const &) and another overload of that function is declared that is
rvalue-ref-qualified (&&) with the same parameter-type-list.
Note: this implies that a member function returning a pointer or reference to its object should be ref-qualified, but not
rvalue-ref-qualified.
Rationale
Returning a reference or pointer to a temporary object, or one of its subobjects, from a member function can lead to immediate dangling.
Ref-qualification of member functions can be used to control which of them can be called on a temporary object:
- A non-const-lvalue-ref-qualified function will never bind to a temporary object; and
- A const-lvalue-ref-qualified function could bind to a temporary object, but this will not occur if an rvalue-ref-qualified
overload is present as it will be preferred during overload resolution.
Compliance with this rule ensures that member functions directly returning references to their object members cannot be called on temporary
objects. This rule is limited to direct references so that checks for compliance are decidable. Use of an indirect reference to a temporary object
after its lifetime has ended is covered by M23_360: MISRA C++ 2023 Rule 6.8.1.
Notes:
- An rvalue-ref-qualified member function will only bind to temporary objects and should therefore never return a reference or pointer
to its object or one of its subobjects.
- This rule does not apply to defaulted assignment operators as they do not have a definition. However, M23_107: MISRA C++ 2023
Rule 8.18.2 prevents the implicitly returned reference from being used.
Example
struct A
{
int32_t a; // a is a subobject of *this
int32_t & b; // b is a reference, not a subobject of *this
int32_t & geta1() & // Compliant - non-const-lvalue-ref-qualified
{ return a; }
int32_t const & geta2() const & // Compliant - const-lvalue-ref-qualified and
{ return a; }
int32_t geta2() && // this rvalue-ref overload exists
{ return a; }
int32_t & getb() // Rule does not apply - b is not a subobject
{ return b; }
A const * getMe1() const & { return this; } // Compliant
void getMe1() const && = delete; // - because this overload exists
A & getMe2() { return *this; } // Non-compliant - not ref-qualified
};
A createA();
// A call to the non-compliant getMe2 on a temporary results in immediate dangling
A & dangling = createA().getMe2();
This rule does not apply to the following example, which is still dangerous and could lead to the use of a dangling pointer (see
M23_360: MISRA C++ 2023 Rule 6.8.1):
class C
{
C * f()
{
C * me = this;
return me; // Indirectly designates 'this'
}
};
In the following example, the instantiation of f in the call at #2 is compliant because #1 is an overload of
f with the same parameter-type-list. However, the instantiation of f in the call at #3 does not have
such an overload and is therefore non-compliant.
struct Tmpl
{
template< typename T >
Tmpl const * f( T ) const & { return this; } // Non-compliant when instantiated
// for #3
void f( int32_t ) const && = delete; // #1
};
void bar( int32_t s32, int8_t s8 )
{
Tmpl tpl;
tpl.f( s32 ); // #2
tpl.f( s8 ); // #3
}
Copyright The MISRA Consortium Limited © 2023