Michael
Michael

Reputation: 7809

Give nullptr a type for template deduction

Suppose the following snippet:

template <class T>
void fct(T* a, T* b){
  // do something
}

A a;
fct(&a, nullptr); // Problem here!

This makes trouble, since the call arguments are of type A* and nullptr_t and so the compiler can not deduce template parameter T.

Generally, I can imagine several ideas how to solve this:

Or is there a more clean solution, like the creation of a something like a "typed nullptr"?

Upvotes: 5

Views: 1683

Answers (4)

Rostislav
Rostislav

Reputation: 3977

You can use the following code:

#include <type_traits>

template<class T>
void f_impl(T*, T*)
{
    std::cout << typeid(T).name() << "\n";
}


template<class T, class U>
void f(T l, U r)
{
    static_assert((std::is_same<T, U>::value && std::is_pointer<T>::value) || 
                  (std::is_same<T, std::nullptr_t>::value && std::is_pointer<U>::value) || // First non-null 
                  (std::is_same<U, std::nullptr_t>::value && std::is_pointer<T>::value)    // Second non-null
                  , "");

    using P = typename std::conditional<std::is_same<T, std::nullptr_t>::value, U, T>::type; 

    f_impl<typename std::remove_pointer<P>::type>(l, r);
}

int main()
{
    int i;
    f(&i, nullptr);
    f(nullptr, &i);
    // f(i, nullptr); // won't compile - non-pointer
    f(&i, &i);

    double d;
    // f(&i, &d); // - won't compile

}

This version tests will allow to call f with one nullptr (but not both), or with two pointers to the same type. With c++14 you can also use things like std::conditional_t, std::remove_pointer_t and std::is_null_pointer to remove some biolerplate.

Upvotes: 1

MSalters
MSalters

Reputation: 179819

As the question already states, nullptr in fact has a type: std::nullptr_t. So just add an explicit overload for specifically that case:

template <class T>
void fct(T* a, std::nullptr_t b) { return fct<T>(a,static_cast<T*>(b)); }

No need to have some template argument class U for that.

Upvotes: 0

MSalters
MSalters

Reputation: 179819

Just make the second argument a non-deduced context, e.g:

template <class T>
void fct(T* a, std::remove_reference<T*>::type b) {

Upvotes: 7

Petr
Petr

Reputation: 9997

I would also suggest the following solution:

template <class T, class U>
void fct(T* a, U b){
  T* b2 = b;
  // do something
}

A a;
fct(&a, nullptr); 

This allows for a wider usage of fct, but maybe that's exactly what you want to.

For example, consider

class A {};
class B : public A {};

...
A a;
B b;
fct(&a, &b); // calls fct<A>
// the following will not compile:
// fct(&b, &a);

Upvotes: 2

Related Questions