Вот пример кода, который отлично работает:
#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 );
}
Однако в приведенном ниже коде компилятор не может соответствовать std :: vector< int> * с nullptr для второго параметра, даже с возможностью вычитать типы шаблонов из первого параметра.
#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 );
}
Сообщение об ошибке:
$ 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 );
Почему это происходит?
Из стандарта C ++ (4.10 Преобразование указателей [conv.ptr])
1 Константа нулевого указателя является целочисленным константным выражением (5.19) типа целого числа
который оценивает в ноль или значение типа std :: nullptr_t. Константа нулевого указателя может быть
преобразован в тип указателя; результатом является значение нулевого указателя этого типа и
отличается от любого другого значения указателя объекта или типа указателя функции. Такой
преобразование называется преобразованием нулевого указателя.
В вашем первом примере ваши два nullptr уже были преобразованы до вывода аргумента шаблона. Так что нет проблем, у вас есть один и тот же тип дважды.
Во втором есть std::vector<int>
и std::nullptr_t
и это не соответствует. Вы должны сделать преобразование самостоятельно: static_cast<std::vector<int>*>(nullptr)
,
Вот как работает вывод шаблона: преобразование не происходит.
Проблема не является эндемичной для nullptr
либо рассмотрим чрезвычайно простой случай:
#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;
}
который доходность:
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);
^
Таким образом, преобразование nullptr
в int*
не происходит предшествующий для вывода аргумента шаблона либо. Как уже упоминалось, у вас есть два способа решения проблемы:
Компилятор не может вывести второй тип аргумента, потому что std::nullptr_t
это не тип указателя.
1 Литерал указателя — это ключевое слово nullptr. Это
prvalue типа std :: nullptr_t. [Примечание: std :: nullptr_t является отличным
тип, который не является ни типом указателя, ни указателем на тип члена;
скорее, значение этого типа является константой нулевого указателя и может быть
преобразуется в значение нулевого указателя или значение указателя нулевого элемента.
[§2.14.7]
вывод аргумента шаблона — сопоставление с образцом Это не делает много преобразования аргументов, кроме преобразования в базу (ну, добавляя const
и справочные классификаторы по типу и decay
).
В то время как nullptr
может быть преобразован в C< D, A >*
это не такой тип. И оба аргумента в равной степени участвуют в выводе.
Вы можете заблокировать удержание второго аргумента, используя что-то вроде typename std::identity<C< D, A > >::type*
и то же самое для первого аргумента. Если вы сделаете это для обоих аргументов, template
типы не будут выведены.
Другой подход состоит в том, чтобы взять два произвольных типа, а затем использовать SFINAE, чтобы гарантировать, что один тип указателя может быть преобразован в другой, и тот, который может быть преобразован в другой, может быть выведен, чтобы быть C<D,A>
для некоторых template
C
и типы D
а также A
, Это, вероятно, соответствует вашей внутренней ментальной модели того, что вычет типа функции должен делать. Тем не менее, результат будет действительно очень подробным.
Еще лучший подход может заключаться в том, чтобы спросить «что вы ожидаете сделать с этими двумя аргументами», и вместо этого проводить тестирование по типу утки вместо сопоставления типов.
Это помешает вам создать шаблон с аргументом nullptr.
Вы, скорее всего, не хотите этого. Вы хотите, чтобы шаблон использовал класс propper в качестве аргумента и принял значение nullptr в качестве значения этого аргумента.
Вы также можете