使用带有 C++20 概念的继承 class 从模板继承 class

Inheriting from a template class using the inheriting class with C++20 concepts

我正在尝试使用 C++20 的 std::default_initializable.

编写类似于 Ogre3Ds Ogre::Singleton 的单例基础 class
#include <concepts>

template <std::default_initializable T>
struct Singleton {};

class Foo: public Singleton<Foo> {};

此代码无法编译:

error C7602: 'Singleton': the associated constraints are not satisfied

我会期待吗?如果不是:为什么?

我正在使用最新的 MSVC /std:c++latest

Compiling this with GCC 给出更有用的错误信息:

...
/opt/compiler-explorer/gcc-10.2.0/include/c++/10.2.0/type_traits:895:52:
error: static assertion failed: template argument must be a complete
class or an unbounded array
895 |       static_assert(std::__is_complete_or_unbounded(__type_identity<_Tp>{}),
    |                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~

特别是,在声明 Singleton<Foo> 时,Foo 还不是一个完整的类型:class 主体还没有打开。因此,编译器无法知道此时是否可以真正满足该概念。

您可以在使用静态断言时看到类似的问题;例如,考虑以下内容(由于同样的原因也无法编译):

template <typename T>
struct Singleton {
    static_assert(std::is_nothrow_destructible_v<T>);
};

class Foo: public Singleton<Foo> {};

如果您无论如何都希望强制执行约束,您仍然可以使用概念:您只需要在类型将是完整类型的位置使用它。例如,您可能 static_assert 在析构函数中包含该概念:

template <typename T>
struct Singleton {
    ~Singleton() {
        static_assert(std::default_initializable<T>);
    }
};

struct Foo : Singleton<Foo> {};             // compiles
struct Bar : Singleton<Bar> { Bar(int); };  // doesn't compile (when used)

(Live)

你不能这样做的原因是因为在将 Foo 作为模板参数提供给 Singleton 的地方,它是一个不完整的类型,并且 incomplete type 根本不可初始化(所以它不能被默认初始化)

这是一个示例程序,它提供了一种更容易理解的错误方式:

template<typename T>
struct A
{
    T value{};
};

struct B : A<B>{};

编译器错误 (GCC 10.2)

<source>:5:7: error: 'A<T>::value' has incomplete type
    5 |     T value{};
      |       ^~~~~
<source>:8:8: note: forward declaration of 'struct B'
    8 | struct B : A<B>
      |        ^