Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++98/03 reference collapsing and cv qualifiers

The code below compiles (gcc 4.7.2 or icc 13) and produces "1 2" output. Which means that const qualifier is dropped, i. e., f<int&> has the parameter type int&.

Why does it happen? As I understand, according to §14.3.1.4:

If a template-argument for a template-parameter T names a type “reference to cv1 S”, an attempt to create the type “reference to cv2 T” creates the type “reference to cv12 S”, where cv12 is the union of the cv-qualifiers cv1 and cv2. Redundant cv-qualifiers are ignored.

const should not be dropped. Here is the code:

#include <iostream>
using namespace std;

template <typename T>
void f(const T& t)
{
    t++;
}

int main()
{
    int a = 1;

    cout << a;
    f<int&>(a);
    cout << ' ' << a << endl;

    return 0;
}
like image 626
user2052436 Avatar asked Feb 07 '13 21:02

user2052436


2 Answers

GCC 4.7.2 does not compile this when the flag -std=c++98 is specified. In fact, in C++98 (as well as in C++03) references to references do not collapse.

An attempt to instantiate f<int&>, where T = int&, produces the following function signature (here I intentionally switch the position of the argument type T and the const specifier, which is allowed because const T& is the same as T const&):

void f(int& const& t) // ERROR: reference to reference is illegal

The above is not legal in C++98, nor in C++03. Consistently, this is the error you get from GCC 4.7.2:

Compilation finished with errors:
source.cpp: In function 'int main()':
source.cpp:15:14: error: no matching function for call to 'f(int&)'
source.cpp:15:14: note: candidate is:
source.cpp:5:6: note: template<class T> void f(const T&)
source.cpp:5:6: note:   template argument deduction/substitution failed:
source.cpp: In substitution of 'template<class T> void f(const T&) [with T = int&]':
source.cpp:15:14:   required from here
source.cpp:5:6: error: forming reference to reference type 'int&'

Nevertheless, if you use the -std=c++11 flag, then the compiler performs reference collapsing when instantiating the template: an lvalue reference to an lvalue reference becomes an lvalue reference:

void f(int& const& t) == void f(int& t)

Here the const qualifier gets dropped, because it applies to the reference, and not to the referenced object. Since references cannot be reassigned, they are const by nature, which is why the const is considered superfluous and removed. See this Q&A on SO for an explanation.

That yields an lvalue reference to an lvalue reference, which resolves into a simple lvalue reference. Hence, the signature on the right side is instantiated.

The above is a viable candidate to resolve the call for f<int&>(a) and, therefore, it compiles without errors.

like image 194
Andy Prowl Avatar answered Sep 28 '22 08:09

Andy Prowl


Here is 1770 where the quote in question seems to originate:

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1770.html

14.3.1 - Template type arguments

-4- If a template-argument for a template-parameter T names a type "lvalue-reference to cv1 S," an attempt to create the type "(lvalue or rvalue) reference to cv2 T" creates the type "lvalue-reference to cv12 S," where cv12 is the union of the cv-qualifiers cv1 and cv2. If the template-argument names a type "rvalue-reference to cv1 S," an attempt to create the type "lvalue-reference to cv2 T" creates the type "lvalue-reference to cv12 S." If the template-argument names a type "rvalue-reference to cv1 S," an attempt to create the type "rvalue-reference to cv2 T" creates the type "rvalue-reference to cv12 S." Redundant cv-qualifiers are ignored.

Here is 2118 where the quote has been struck out:

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2118.html

14.3.1 - Template type arguments

-4- If a template-argument for a template-parameter T names a type "reference to cv1 S" that is a reference to a type A, an attempt to create the type "reference to cv2 T" "lvalue-reference to cv T" creates the type "reference to cv12 S", where cv12 is the union of the cv-qualifiers cv1 and cv2. Redundant cv-qualifiers are ignored "lvalue-reference to A", while an attempt to create the type "rvalue-reference to cv T" creates the type T.

What you are quoting seems to be obsolete wording.

like image 32
Yakk - Adam Nevraumont Avatar answered Sep 28 '22 10:09

Yakk - Adam Nevraumont