【问题标题】:In clang, why does this template default parameter require the destructor to be instantiated?在clang中,为什么这个模板默认参数需要实例化析构函数?
【发布时间】:2021-11-06 03:03:09
【问题描述】:
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;

bar 有一个调用转发构造函数的默认参数也是constexpr 时,编译器也会尝试实例化析构函数,但失败了,因为T::foo 不能被调用,因为T 是不完整的类型.

如果默认参数没有调用转发构造函数(例如,如果我们将5.0 更改为5),如果转发构造函数不是constexpr,或者(当然)如果类型T 至此完成。

如果析构函数是constexpr,也会出现问题,即使构造函数没有转发。

noexcept 似乎无关紧要,但我将其保留以确保编译器不会尝试生成堆栈展开代码。

这只发生在 clang (12.0.1) 上,而不是 gcc (11.2) 或 Visual Studio (19.29)。见godbolt

请注意,函数bar没有定义或调用

为什么这不能在 clang 中编译?

【问题讨论】:

  • 听起来像一个错误,默认参数是在调用者的上下文中评估的。可能相关:bugs.llvm.org/show_bug.cgi?id=42303
  • 似乎是作为类模板实例化的一部分,[temp.inst]/11 可能允许,它试图确定 problem_type(double) 是否实际上是该专业化的 constexpr

标签: c++ language-lawyer


【解决方案1】:

首先请注意,Clang 在这里更加一致,因为 GCC 和 Clang 都拒绝了一个类似的示例,其中默认初始化类字段,其中析构函数体仍然引用不完整的类型:

struct incomplete_type;

template<typename T>
struct problem_type
{
    constexpr problem_type(int) noexcept {};
    constexpr problem_type(double) noexcept : problem_type(5) {}

    constexpr ~problem_type() {
        T::foo();
    }
};

// ok in all compilers
struct A {
    problem_type<incomplete_type> p;
};

// error in GCC and Clang
struct B {
    problem_type<incomplete_type> p = 5.0;
};

演示:https://gcc.godbolt.org/z/59hW3ecex

在您的示例中,problem_type&lt;incomplete_type&gt; 的析构函数在函数调用期间被调用,因此编译器必须检查其现有定义。看起来只有 Clang 能正确地做到这一点。

为了让你的例子被所有编译器接受,请在函数bar之后在类主体之外定义析构函数~problem_type

struct incomplete_type;

template<typename T>
struct problem_type
{
    constexpr problem_type(int) noexcept {};
    constexpr problem_type(double) noexcept : problem_type(5) {}
    constexpr ~problem_type();
};

void bar(problem_type<incomplete_type> arg=5.0) noexcept;

template<typename T>
constexpr problem_type<T>::~problem_type() {
    T::foo();
}

演示:https://gcc.godbolt.org/z/rvzMbbz9d

但是你仍然需要在你调用bar函数的地方完成incomplete_type

【讨论】:

猜你喜欢
  • 1970-01-01
  • 2020-07-10
  • 1970-01-01
  • 2021-10-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-25
相关资源
最近更新 更多