2015-10-01 2 views
5

Предположим, следующий фрагмент кода:Дайте nullptr тип для шаблона вычета

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

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

Это делает неприятности, так как аргументы вызова имеют тип A* и nullptr_t и поэтому компилятор не может вывести параметр шаблона T.

Вообще, я могу представить несколько идей, как решить эту проблему:

  • Определение A* b = nullptr и использовать fct(&a, b)
  • Определение перегрузки с одним аргументом для fct для nullptr случае
  • Использование fct(&a, static_cast<A*>(nullptr))

Или есть более чистое решение, li ke создание чего-то вроде «типизированного nullptr»?

+0

Как ваш желаемый «типизированный nullptr» может отличаться от 'static_cast (nullptr)'? – Petr

+2

Ваша вторая альтернатива выглядит как лучшая ставка ... перегрузка (специализация шаблона) – basav

+0

Вы уже ответили на вопрос ... 1-я и 3-я альтернативы более или менее одинаковы: вам нужен T *, а nullptr - это не так, что вы запрещаете ' fct (& a, nullptr) ', а второй использует явную перегрузку для его разрешения. Я не могу представить другого ответа ... –

ответ

2

Я также хотел бы предложить следующее решение:

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

A a; 
fct(&a, nullptr); 

Это позволяет для более широкого использования fct, но, возможно, это именно то, что вы хотите.

Для примера рассмотрим

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

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

Вы можете использовать следующий код:

#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 

} 

Эта версия тесты позволяют назвать f с одним nullptr (но не оба), или с двумя указателями к тому же типу. С помощью C++ 14 вы также можете использовать такие вещи, как std::conditional_t, std::remove_pointer_t и std::is_null_pointer, чтобы удалить некоторые biolerplate.

+0

Вам нужно вызвать 'f ' или 'f ' в зависимости от того, что не является нулевым; ваша текущая версия вызывает 'f ' при вызове как 'f (nullptr, & i)'. – Petr

+0

@Petr С этим кодом было больше проблем. Исправлены ошибки. Благодаря! – Rostislav

5

Просто сделать второй аргумент не-вывод, контекст, например:

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

Отсутствует 'typename' перед вторым аргументом функции? Можно также использовать более новую и немного менее verbose'std :: remove_reference_t'. – user1735003

0

Поскольку вопрос уже говорится, nullptr на самом деле имеет тип: std::nullptr_t. Так что просто добавить явную перегрузку специально для этого случая:

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

Нет необходимости иметь некоторый аргумент шаблона class U для этого.

Смежные вопросы