在函数调用中,为什么nullptr不匹配指向模板对象的指针?

时间:2013-12-05 16:37:57

标签: c++ c++11 function-call template-matching 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 > *sample1 = nullptr;
  std::vector< int > *sample2 = nullptr;
  foo( sample1, sample2 );
  return( 0 );
}

但是,在下面的代码中,编译器无法匹配std :: vector&lt;对于第二个参数,int&gt; * with 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 );

为什么会这样?

5 个答案:

答案 0 :(得分:6)

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

  

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

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

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

答案 1 :(得分:5)

这就是模板扣除的工作方式:不进行转换。

问题不是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;
}

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

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

  • 指定模板参数(因此不会发生扣减)
  • 自己转换参数(扣除发生,但在显式转换后)

答案 2 :(得分:2)

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

  

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

答案 3 :(得分:0)

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

虽然nullptr可以转换为C< D, A >*,但它不是这种类型。这两个论点同样参与演绎。

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

另一种方法是采用两种任意类型,然后使用SFINAE确保一种类型的指针可以转换为另一种类型,而可以转换为另一种指针的指针可以推导为{对于某些C<D,A> template和类型CD,{1}}。这可能与函数类型推导应该所做的内部心智模型相匹配。但是,结果真的非常冗长。

更好的方法可能是问“你对这两个参数有什么期望”,并对其进行鸭式测试,而不是进行类型匹配。

答案 4 :(得分:-1)

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

你可以

  • 明确调用模板的正确版本
  • 将nullptr强制转换为模板的propper类型。
  • 创建一个右指针类型的局部变量,给它赋值nullptr并使用该变量进行调用。