默认成员值和不完整类型的模板扩展中的编译器差异

Gre*_*reg 8 c++ compiler-errors language-lawyer

在为MSVC和Clang之间具有默认初始化程序的类成员扩展模板时,似乎有所不同,这有时可能导致代码在MSVC中成功编译但在Clang中失败。

有问题的问题代码相当复杂,并且分布在多个文件中,但是我认为以下玩具示例显示出相同的差异:

#include <memory>

class Impl;

class A {
  std::unique_ptr<Impl> ptr = nullptr;
public:
  A();
  ~A();
};

int main() {}
Run Code Online (Sandbox Code Playgroud)

https://godbolt.org/z/3s5Drh

从编译器资源管理器中可以看出,Clang给出了此代码的错误。如果= nullptr删除,则两个编译器都将运行而不会出现错误。

显然,该代码不会执行任何操作,即使执行了此操作也没= nullptr必要。不过我很好奇,关于标准中是否有任何说明这种情况下的一个或多个编译器正确的?

Jon*_*ely 7

这里涉及多个错误,请参阅https://bugs.llvm.org/show_bug.cgi?id=39363#c8

因此:GCC中有两个错误(尽管可能是同一回事),Clang的C ++ 17支持中有一个错误,libstdc ++中有一个错误,而libc ++中没有任何错误。将其重新定位为Clang错误。:)

libstdc ++实际上是该标准中的一个缺陷,该缺陷已由https://wg21.link/lwg2081意外修复(有关更多详细信息,请参见https://gcc.gnu.org/bugzilla/show_bug.cgi?id=87704)。

我认为该程序在C ++ 14模式下实际上是无效的,因为默认成员初始化程序使用了复制初始化,因此创建了一个临时对象,然后将其移出,然后将其销毁。销毁临时对象意味着应该实例化析构函数,这需要完整的类型。在C ++ 17模式下,保证复制省略意味着没有临时的,因此没有析构函数实例化,并且代码应该有效。但是,即使在C ++ 17模式下,GCC和Clang都做错了事。

如果您使用直接列表初始化而不是复制初始化,则它可与Clang一起使用:

std::unique_ptr<Impl> ptr{nullptr};
Run Code Online (Sandbox Code Playgroud)

这也适用:

std::unique_ptr<Impl> ptr{};
Run Code Online (Sandbox Code Playgroud)

和等效的:

std::unique_ptr<Impl> ptr = {};
Run Code Online (Sandbox Code Playgroud)

而且,根本不提供初始化程序也可以,并且也可以与GCC一起使用:

std::unique_ptr<Impl> ptr;
Run Code Online (Sandbox Code Playgroud)

  • “ *,因为默认成员初始化程序使用复制初始化*”。好吧,我也这么认为。但是,所有应该发生的地方是A :: A(),它定义在另一个TU中。那么,我们真的需要在类定义中遇到错误吗(即使构造函数将被定义到另一个TU中)? (2认同)