Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Choosing between template instantiation with pointer arguments

Look at the following test code:

template<class T> struct Wrap {};

template<typename T> inline
void fun (T *&Int)          // **choice 1**
{}

template<typename T> inline
void fun (Wrap<T> *&Int)    // **choice 2**
{}

int main()
{
  int i = 6;
  fun((char*&)(i));         // **call 1**
  fun((Wrap<char>*&)(i));   // **call 2**
}

When I run this code in linux g++, it works as per expectations. When fun() is called with char*&, it calls function of choice 1 straight forward. However, I am interested when we call fun() with Wrap< char >*& and it calls the choice 2. Even though choice 1 and 2 both appear valid for the 2nd call, compiler manages to choose little better contender -> choice 2 (because it exists).

Question: Is it guaranteed that, the same behavior will be retained for any other compiler for C++ ? If not, then is there any other alternative to make it deterministic?

like image 415
iammilind Avatar asked Mar 28 '11 06:03

iammilind


3 Answers

The second choice is chosen because it's more specialized than the first- that is, T*& can bind to any non-temporary T*, but Wrap<T>*& can only bind to a non-temporary Wrap<T>*. This is Standard as far as I know and should be portable behaviour but what is and isn't portable in practice when it comes to this sort of thing is often not the definition of what's Standard.

like image 117
Puppy Avatar answered Nov 16 '22 03:11

Puppy


Someone with a better knowledge of the spec can confirm this, but I believe that as Wrap<T> is a more specific type than simply T, call 2 will always resolve to 'choice 2', on all platforms compilers.

like image 3
Gian Paolo Avatar answered Nov 16 '22 04:11

Gian Paolo


While the code might look like a template specialization, that is not the case. The language does not allow for partial template function specializations. The two are unrelated templates that happen to be overloads.

The compiler will lookup up the call to fun( (Wrap<char>*&) i ) with the usual lookup mechanisms, will find the two templates and will determine that there are two potential overloads:

template <typename T> void fun( T*& );      // with T == Wrap<char>
template <typename T> void fun( Wrap<T>*& ) // with T == char

Overload resolution will then determine that the second is a better match and instantiate it. This is guaranteed by the standard, but beware: they are not the same template, but rather different templates and you might run into undexpected results. Look at the article @LiKao linked for more insight.

like image 2
David Rodríguez - dribeas Avatar answered Nov 16 '22 04:11

David Rodríguez - dribeas