默认概念约束函数从未选择用于实例化

Hum*_*ler 4 c++ c++-concepts c++20

在使用 C++20 时,我遇到了一个奇怪的情况,我不确定这是由于 C++20 的婴儿期而导致的编译器缺陷,或者这是否是正确的行为。

问题在于,特定的约束函数要么没有正确选择,要么产生编译错误——完全取决于定义的顺序。

这种情况发生在特定情况下:

  • 构造函数/析构函数/成员函数受requires, 和约束
  • 对于不满足T子句的某些实例此构造函数/析构函数/成员函数将被隐式删除requires

例如,考虑这个基本naive_optional<T>实现:

template <typename T>
class naive_optional {
public:
    naive_optional() : m_empty{}, m_has_value{false}{}

    ~naive_optional() requires(std::is_trivially_destructible_v<T>) = default;
    ~naive_optional() {
        if (m_has_value) {
            m_value.~T();
        }
    }
private:
    struct empty {};
    union {
        T m_value;
        empty m_empty;
    };
    bool m_has_value;
};
Run Code Online (Sandbox Code Playgroud)

此代码适用于普通类型,例如naive_optional<int>,但无法实例化非普通类型,例如naive_optional<std::string>由于隐式删除的析构函数:

<source>:26:14: error: attempt to use a deleted function
    auto v = naive_optional<std::string>{};
             ^
<source>:8:5: note: explicitly defaulted function was implicitly deleted here
    ~naive_optional() requires(std::is_trivially_destructible_v<T>) = default;
    ^
<source>:17:11: note: destructor of 'naive_optional<std::basic_string<char>>' is implicitly deleted because variant field 'm_value' has a non-trivial destructor
        T m_value;
          ^
1 error generated.
Run Code Online (Sandbox Code Playgroud)

Live Example

如果更改定义的顺序,使受约束函数位于无约束函数之下,则效果很好,但每次都会选择无约束函数:

<source>:26:14: error: attempt to use a deleted function
    auto v = naive_optional<std::string>{};
             ^
<source>:8:5: note: explicitly defaulted function was implicitly deleted here
    ~naive_optional() requires(std::is_trivially_destructible_v<T>) = default;
    ^
<source>:17:11: note: destructor of 'naive_optional<std::basic_string<char>>' is implicitly deleted because variant field 'm_value' has a non-trivial destructor
        T m_value;
          ^
1 error generated.
Run Code Online (Sandbox Code Playgroud)

Live Example


我的问题最终是:这种行为是否正确——我是否滥用了requires?如果这是正确的行为,是否有任何简单的方法可以有条件地完成default- 本身不是模板的构造函数/析构函数/函数?我希望避免继承基类实现的“传统”方法。

Bar*_*rry 8

您的代码是正确的,并且确实是支持此类事情的激励示例(请参阅P0848,我是其中的作者之一)。Clang 只是还没有实现此功能(正如您在此处看到的那样)。

gcc 和 msvc 都接受您的代码


请注意,您不再需要了emptyunion { T val; }在 C++20 中就足够了,这要感谢P1331演示

  • 哇,谢谢,这确实是一个很棒的答案——它很简洁,并提供了所有必要的来源/细节。我没有检查 MSVC,但我确实注意到 `gcc` 接受了代码,这就是为什么我不确定这是语言还是编译器问题(我可能应该将其添加到问题中)。了解“union { T val;”也很棒。}`——这对我来说是全新的。 (2认同)