我已经在这里和这里检查了问题,但仍然无法弄清楚出了什么问题.
这是调用代码:
#include "lib.h"
using namespace lib;
int
main(const int argc, const char *argv[])
{
return 0;
}
Run Code Online (Sandbox Code Playgroud)
这是lib代码:
#ifndef lib_h
#define lib_h
#include <string>
#include <vector>
#include <memory>
namespace lib
{
class Foo_impl;
class Foo
{
public:
Foo();
~Foo();
private:
Foo(const Foo&);
Foo& operator=(const Foo&);
std::unique_ptr<Foo_impl> m_impl = nullptr;
friend class Foo_impl;
};
} // namespace
#endif
Run Code Online (Sandbox Code Playgroud)
clang ++给了我这个错误:
将'sizeof'无效应用于不完整类型'lib :: Foo_impl'
注意:在成员函数'std :: default_delete :: operator()'的实例化中请求
你可以看到我已经特别声明了Foo析构函数.我还缺少什么?
struct incomplete_type;
#if 0
struct incomplete_type {
static void foo() {}
};
#endif
template<typename T>
struct problem_type
{
constexpr problem_type(int) noexcept {};
constexpr problem_type(double) noexcept : problem_type(5) {}
~problem_type() {
T::foo();
}
};
void bar(problem_type<incomplete_type> arg=5.0) noexcept;
Run Code Online (Sandbox Code Playgroud)
当bar有一个调用转发构造函数的默认参数也是 时constexpr,编译器还会尝试实例化析构函数,但会失败,因为T::foo无法调用,因为T它是不完整类型,
如果默认参数不调用转发构造函数(例如,如果我们更改5.0为5),如果转发构造函数不是constexpr,或者(当然)如果类型T此时类型已完成,则不会出现此问题。
constexpr如果析构函数是,即使构造函数没有转发,也会出现该问题。
noexcept似乎无关紧要,但我将其保留下来以确保编译器不会尝试生成堆栈展开代码。
这只发生在 clang (12.0.1) 上,而不发生在 gcc (11.2) 或 Visual Studio (19.29) 上。见神箭
请注意,该函数bar 未定义或调用。
为什么这不能在 clang 中编译?
请考虑一个A具有带默认初始值设定项的类型字段u的结构U<R>。析构函数~U<R>仅声明:
template<typename T>
struct U {
~U();
};
struct R;
struct A {
U<R> u = U<R>{};
};
Run Code Online (Sandbox Code Playgroud)
所有编译器都接受此代码,演示: https: //gcc.godbolt.org/z/oqMjTovMo
但是如果我们将析构函数定义~U<R>如下:
template<typename T>
struct U {
~U() { static_assert( sizeof(T) > 0 ); }
};
Run Code Online (Sandbox Code Playgroud)
那么当前的编译器就会出现分歧。MSVC 不断接受程序,而 GCC/Clang 打印错误
error: invalid application of 'sizeof' to an incomplete type 'R'
Run Code Online (Sandbox Code Playgroud)
演示: https: //gcc.godbolt.org/z/713TzPd6v
显然,编译器必须验证默认初始化类成员的析构函数的可用性,以防在构造过程中发生异常。但标准是否要求编译器只检查析构函数的可用性(如 MSVC 所做的那样),还是编译器也应该验证其主体?RMSVC 行为在这里看起来更方便,因为它允许在结构定义时进行前向声明A。
PS:这个探索不仅仅具有纯粹的理论意义。如果将U此处替换为std::unique_ptrthen ,则可以解释为什么类型的类字段std::unique_ptr<R>被 MSVC …