为啥 `void* = 0` 和 `void* = nullptr` 会有所不同?

Posted

技术标签:

【中文标题】为啥 `void* = 0` 和 `void* = nullptr` 会有所不同?【英文标题】:Why `void* = 0` and `void* = nullptr` makes the difference?为什么 `void* = 0` 和 `void* = nullptr` 会有所不同? 【发布时间】:2018-11-18 19:34:52 【问题描述】:

我在玩 SFINAE,发现我无法解释的行为。

这个compiles fine:

template<typename Integer,
         std::enable_if_t<std::is_integral<Integer>::value>* = nullptr>
void foo(Integer) 

template<typename Floating,
         std::enable_if_t<std::is_floating_point<Floating>::value>* = nullptr>
void foo(Floating) 

虽然这个(nullptr 替换为 0):

template<typename Integer,
         std::enable_if_t<std::is_integral<Integer>::value>* = 0>
void foo(Integer) 

template<typename Floating,
         std::enable_if_t<std::is_floating_point<Floating>::value>* = 0>
void foo(Floating) 

gives me a compile error:

prog.cpp: In function ‘int main()’: prog.cpp:13:10: error: no matching function for call to ‘foo(int)’
     foo(3);
          ^ prog.cpp:5:6: note: candidate: template<class Integer, std::enable_if_t<std::is_integral<_Tp>::value>* <anonymous> > void foo(Integer)  void foo(Integer) 
      ^~~ prog.cpp:5:6: note:   template argument deduction/substitution failed: prog.cpp:4:64: error: could not convert template argument ‘0’ to ‘std::enable_if_t<true, void>* aka void*’
          std::enable_if_t<std::is_integral<Integer>::value>* = 0>
                                                                ^ prog.cpp:9:6: note: candidate: template<class Floating, std::enable_if_t<std::is_floating_point<_Tp>::value>* <anonymous> > void foo(Floating)  void foo(Floating) 
      ^~~ prog.cpp:9:6: note:   template argument deduction/substitution failed: prog.cpp:8:71: note: invalid template non-type parameter
          std::enable_if_t<std::is_floating_point<Floating>::value>* = 0>
                                                                       ^

enable_if_t 在没有替换失败的情况下扩展为void,所以在模板参数列表中我会有类似void* = 0 的内容。为什么它会破坏编译?..

【问题讨论】:

因为0intnullptrvoid *。您可能可以将 0 转换为 (void *) 以使其工作。 @tdk001: "nullptrvoid *",错了,是std::nullptr_t 您可以使用template&lt;void* = 0&gt; void foo() 减少问题。 clang 给出错误 "null 非类型模板参数必须强制转换为模板参数类型'void *'"。 Demo 【参考方案1】:

默认模板参数遵循它们自己的更严格的转换规则。不应用 0 到指针类型的转换。

见[temp.arg.nontype]/5.2(强调我的):

对于指向对象的类型指针的非类型模板参数,应用限定转换([conv.qual])和数组到指针转换([conv.array]);如果模板参数是 std::nullptr_t 类型,则应用空指针转换 ([conv.ptr])。

[ 注意: 特别是,零值整型常量表达式 ([conv.ptr]) 的空指针转换和派生到基数的转换 ([conv.ptr] ) 被应用。 虽然0 是整数类型的非类型模板参数的有效模板参数,但它不是指针类型的非类型模板参数的有效模板参数。 但是,(int*)0nullptr 都是“指向 int 的指针”类型的非类型模板参数的有效模板参数。 ——尾注]

【讨论】:

很好的发现。 C++ 有这么多这样不明显的规则,想到所有即席逻辑编译器编写者必须处理的事情就很可怕。 我认为标准基本上想说“C中没有模板,所以至少那里我不必符合C”:)跨度> 这个决定背后的动机是什么?防止不需要的隐式转换什么的? 请注意,即使标准设置为最新,最新的 MSVC 仍然不强制执行此规则。 @rustyx:有人说笔记不规范,不能用来证明任何事情。因此,我不得不多次编辑/删除我的答案。那么如何处理这种情况呢?

以上是关于为啥 `void* = 0` 和 `void* = nullptr` 会有所不同?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我们需要 void 函数? [关闭]

为啥 `boost::any` 比 `void*` 更好?

为啥 std::transform 和类似的东西将“for”循环增量转换为(void)?

为啥 pthread_join 会出现此错误? [错误] 从 'void*' 到 'void**' 的无效转换 [-fpermissive]

变量或字段 'nameOfVariable' 声明为 void 不知道为啥 :(

为啥不可能有一个对 void 的引用?