10

这是一个完美运行的代码示例:


#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 );

为什么会这样?

4

5 回答 5

8

这就是模板推导的工作方式:不发生转换。

这个问题也不是地方性的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);
                 ^

因此,to 的转换nullptrint*不会发生模板参数推导之前。如前所述,您有两种解决问题的方法:

  • 指定模板参数(因此不会发生扣除)
  • 自己转换参数(扣除发生,但在你显式转换之后)
于 2013-12-05T19:05:46.150 回答
7

来自 C++ 标准(4.10 指针转换 [conv.ptr])

1 空指针常量是整数类型的整数常量表达式 (5.19) 纯右值,其计算结果为零或 std::nullptr_t 类型的纯右值。空指针常量可以转换为指针类型;结果是该类型的空指针值,并且可以与对象指针或函数指针类型的所有其他值区分开来。这种转换称为空指针转换。

在您的第一个示例中,您的两个 nullptr 在模板参数推导之前已经被转换。所以你有两次相同的类型没有问题。

在第二个中,有一个std::vector<int>和一个std::nullptr_t不匹配的。您必须自己进行转换:static_cast<std::vector<int>*>(nullptr).

于 2013-12-05T16:51:12.463 回答
3

编译器无法推断出第二个参数类型,因为std::nullptr_t它不是指针类型。

1指针文字是关键字 nullptr。它是 std::nullptr_t 类型的纯右值。[注意:std::nullptr_t 是一个独特的类型,既不是指针类型也不是指向成员类型的指针;相反,这种类型的纯右值是一个空指针常量,可以转换为空指针值或空成员指针值。[§2.14.7]

于 2013-12-05T16:54:42.020 回答
0

模板参数推导是模式匹配。除了转换为基础之外,它不会对参数进行太多转换(嗯,const在类型 and 上添加和引用限定符decay)。

虽然nullptr可以转换为 a C< D, A >*,但它不是这样的类型。两个论点都平等地参与了推论。

您可以通过使用类似的东西来阻止第二个参数的推导typename std::identity<C< D, A > >::type*,并且对于第一个参数也是如此。如果对两个参数都这样做,template则不会推断出类型。

另一种方法是采用两种任意类型,然后使用 SFINAE 确保一种类型的指针可以转换为另一种类型,并且可以将可以从另一种类型转换为的一种类型可以推导出为 a C<D,A>for some template Cand typesDA这可能与您对函数类型推导应该做什么的内部心理模型相匹配。但是,结果将非常非常冗长。

一个更好的方法可能是询问“您希望对这两个参数做什么”,并对此进行鸭式测试,而不是进行类型匹配。

于 2013-12-05T19:34:37.487 回答
-1

这是为了防止您创建一个以 nullptr 作为参数的模板。你很可能不希望那样。您希望模板使用 propper 类作为参数,并将 nullptr 作为该参数的值。

你可以

  • 显式调用模板的正确版本
  • 将 nullptr 强制转换为模板的适当类型。
  • 创建一个正确指针类型的本地 var,为其赋值 nullptr 并使用该 var 进行调用。
于 2013-12-05T16:47:50.157 回答