Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In function call, why doesn't nullptr match a pointer to a template object?

Here is an example of a code that works perfectly:


#include<iostream>
#include<vector>

template< class D, template< class D, class A > class C, class A = std::allocator< D > >
void foo( C< D, A > *bar, C< D, A > *bas ) {
  std::cout << "Ok!" << std::endl;
}

int main( ) {
  std::vector< int > *sample1 = nullptr;
  std::vector< int > *sample2 = nullptr;
  foo( sample1, sample2 );
  return( 0 );
}

In the code below, however, the compiler is unable to match std::vector< int >* with nullptr for the second parameter, even being able to deduct the template types from the first parameter.


#include<iostream>
#include<vector>

template< class D, template< class D, class A > class C, class A = std::allocator< D > >
void foo( C< D, A > *bar, C< D, A > *bas ) {
  std::cout << "Ok!" << std::endl;
}

int main( ) {
  std::vector< int > *sample = nullptr;
  foo( sample, nullptr );
  return( 0 );
}

The error message is:


$ g++ -std=c++11 nullptr.cpp -o nullptr

nullptr.cpp: In function ‘int main()’:

nullptr.cpp:11:24: error: no matching function for call to ‘foo(std::vector<int>*&, std::nullptr_t)’

   foo( sample, nullptr );

nullptr.cpp:11:24: note: candidate is:

nullptr.cpp:5:6: note: template<class D, template<class D, class A> class C, class A> void foo(C<D, A>*, C<D, A>*)

 void foo( C< D, A > *bar, C< D, A > *bas ) {

nullptr.cpp:5:6: note:   template argument deduction/substitution failed:

nullptr.cpp:11:24: note:   mismatched types ‘C<D, A>*’ and ‘std::nullptr_t’

   foo( sample, nullptr );

Why does that happen?

like image 274
user1574855 Avatar asked Dec 05 '13 16:12

user1574855


3 Answers

This is just how template deduction work: no conversion take place.

The problem is not endemic to nullptr either, consider the extremely simple case:

#include <iostream>

struct Thing {
    operator int() const { return 0; }
} thingy;

template <typename T>
void print(T l, T r) { std::cout << l << " " << r << "\n"; }

int main() {
    int i = 0;
    print(i, thingy);
    return 0;
}

which yields:

prog.cpp: In function ‘int main()’:
prog.cpp:12:17: error: no matching function for call to ‘print(int&, Thing&)’
  print(i, thingy);
                 ^
prog.cpp:12:17: note: candidate is:
prog.cpp:8:6: note: template<class T> void print(T, T)
 void print(T l, T r) { std::cout << l << " " << r << "\n"; }
      ^
prog.cpp:8:6: note:   template argument deduction/substitution failed:
prog.cpp:12:17: note:   deduced conflicting types for parameter ‘T’ (‘int’ and ‘Thing’)
  print(i, thingy);
                 ^

Thus, the conversion of nullptr to int* does not occur prior to template argument deduction either. As mentioned, you have two ways of solving the issue:

  • specifying the template parameters (thus no deduction occur)
  • converting the argument yourself (deduction occurs, but after your explicit conversion)
like image 172
Matthieu M. Avatar answered Oct 03 '22 23:10

Matthieu M.


From the C++ standard (4.10 Pointer conversions [conv.ptr])

1 A null pointer constant is an integral constant expression (5.19) prvalue of integer type that evaluates to zero or a prvalue of type std::nullptr_t. A null pointer constant can be converted to a pointer type; the result is the null pointer value of that type and is distinguishable from every other value of object pointer or function pointer type. Such a conversion is called a null pointer conversion.

In your first exemple your two nullptr have already been converted before template argument deduction. So there is no problem you have the same type twice.

In the second one, there is a std::vector<int> and a std::nullptr_t and that does not match. You have to do the conversion yourself: static_cast<std::vector<int>*>(nullptr).

like image 30
Johan Avatar answered Oct 04 '22 00:10

Johan


Compiler can not deduce the second argument type because std::nullptr_t is not a pointer type.

1 The pointer literal is the keyword nullptr. It is a prvalue of type std::nullptr_t. [Note: std::nullptr_t is a distinct type that is neither a pointer type nor a pointer to member type; rather, a prvalue of this type is a null pointer constant and can be converted to a null pointer value or null member pointer value. [§2.14.7]

like image 38
masoud Avatar answered Oct 03 '22 23:10

masoud