21

In a C++ template with the generic type T, I can use

const T & 

to get a reference to a constant T. However, if now T itself is a reference type (like e.g. T = int &), the above term resolves to

int & 

and not to

const int & 

which quite makes sense, since any reference itself is always constant. However, is there still a way to require a

const T & 

if T itself is a reference type?

Edit: sample code to evaluate (g++ compiler):

template <typename T> class TemplateClass { public: void foo(const T &bar) { } }; int main() { TemplateClass<int &> x; x.foo(0); // <-- compile error: no conversion from int to int& return 0; } 
8
  • 5
    I don't think it resolves to int&. Why do you think that it does? Commented Nov 28, 2011 at 21:39
  • @MathiasKunter: What makes you think g++ says it does? Commented Nov 28, 2011 at 21:41
  • 2
    @NicolBolas: actually it seems that he's right... ideone.com/h7PsC Commented Nov 28, 2011 at 21:42
  • 2
    But: Comeau says that it's incorrect. So, probably it's a g++ bug. Commented Nov 28, 2011 at 21:44
  • 1
    GCC is (even in pedantic mode, apparently?) allowing this by extension. A reference to a reference should be an error, and reference collapsing rules weren't introduced until C++11. See here. Commented Nov 28, 2011 at 21:48

3 Answers 3

25

Remove the reference:

template<typename T> void Test(const typename std::remove_reference<T>::type & param) { param = 20; } 

Now it works as expected.

Sign up to request clarification or add additional context in comments.

1 Comment

This is both awesome, and messed up.
10

You can always use template specialisation to implement a different version for any kind of reference:

template <typename T> struct X { void foo(T const&); }; template <typename T> struct X<T&> { void foo(T const&); }; 

Now, X<int>::foo expects an int const& and X<int&>::foo expects an int const&, too.

However, it is not entirely clear from your question what you are trying to do exactly.


Edit: My g++ version (4.6.1) does not complain without template specialisation for the following

int i = 7; X<int&>(i); 

While it does for

X<int&>(7); 

Which is correct IMO, because you try to convert a temporary (7) to a mutable reference (even if that is a reference to a const reference).


Edit 2: If you want to reduce duplicate code, then do not specialise your original class, but use this:

template <typename T> struct R { typedef T& Ref; typedef T const& ConstRef; }; template <typename T> struct R<T&> { typedef T& Ref; typedef T const& ConstRef; }; template<typename T> struct X { void foo(typename R<T>::ConstRef x); }; 

7 Comments

Yes this would indeed solve the problem. I however would like to preserve the overhead of using a template specialization if possible.
Yes, what I'm trying to achieve is that X<int&>(7) is also valid (which it would be if the parameter's type was a const int & instead of int &). But it seems that template specialisation is the only way to go here, right?
@MathiasKunter: Yes. Either use my minimal R type (change name for production code) or std::remove_reference which presumably does precisely the same trick (but there you would have to add const& yourself).
Okay just saw your second edit. Thanks, that sould make it. Great solution IMHO.
@MathiasKunter: You can use traits as per the second edit, but those are implemented in terms of specializations... but at least it will remove the need for duplicate implementations. You can use typename const std::remove_reference<T>::type & as your argument type (I would recommend using a typedef for that...
|
1

I have encountered the very same problem. It seems that the '&' type conversion operator binds stronger than the 'const' qualifier. So when we have this code:

template<class t> void fun(const t i) { } fun<int&>(); 

the function ends up with type void(int&), not the expected void(const int&).

To solve this problem, I have defined this template:

template<class t> struct constify { typedef t type; }; template<class t> struct constify<t&> { typedef const t& type; }; 

Now define your function as:

template<class t> void fun(typename constify<t>::type i) { } fun<int&>(); 

The instantiated function will have the type void(const int&), as needed.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.