[编辑] 这在 g++ 10.3、11.2 和当前的 clang 上按预期工作。一些 cmets 表明这是未定义的行为,因此请注意未来编译器可能出现的意外更改。
原解决方案:
我也只是偶然发现了这一点,并通过允许类型不完整或具有所需约束的完整,成功地使我的 CRTP 与概念一起工作。
除了定义的IsFoo,我还定义了IsComplete helper(使用sizeof技巧),最后,IsFooIncomplete以如下方式:
template<class T>
concept IsFooIncomplete = !IsComplete<T> || IsFoo<T>;
这样我可以确保在 Foo 的处理过程中,它是不完整的,并且在 class 完成后,它是完整的并且匹配所需的 IsFoo 约束。
#include <concepts>
#include <type_traits>
template<class T>
concept IsFoo = requires(T self)
{
{
self.a
} -> std::same_as<int&>;
};
template<class T>
concept IsComplete = requires(T self)
{
{
// You can't apply sizeof to an incomplete type
sizeof(self)
};
};
template<class T>
concept IsFooIncomplete = !IsComplete<T> || IsFoo<T>;
#if 0
// Will not compile, because Foo currently has incomplete type
template<IsFoo AFoo>
struct AcceptsFoo
{};
#else
// will compile with IsFooIncomplete
template<IsFooIncomplete AFoo> // no need to use 'class AFoo' here...
struct AcceptsFoo
{};
#endif
struct Foo
{
int a;
int b;
// Foo is incomplete here, but that's fine!
static_assert(!IsComplete<Foo>);
AcceptsFoo<Foo> obj;
};
// Foo is now complete, and that's also fine!
static_assert(IsFoo<Foo>);
在g++ 版本10.3.0(带有标志--std=c++20)上运行良好,我希望它也适用于其他编译器。
[EDIT] 正如 cmets 中所指出的,这接受任何不完整的类型,但这是有意的。只有外部静态断言会过滤完整的类型案例。感谢@David Herring 在 Bar 上的示例,我在这里写了它以进行测试:https://godbolt.org/z/sqc75qqMv
[EDIT2] 现在它处理 CRTP 变体,没有任何未定义的行为,也没有 IsComplete 解决方法,只需存储类型以在类完成后进行检查。
// Will not compile, because Foo currently has incomplete type
template<IsFoo AFoo>
struct AcceptsFoo
{};
#else
template<class AFoo> // will not check IsFoo directly here...
struct AcceptsFoo
{
using IsFooType = AFoo; // will store type on IsFootType for later checks
};
#endif
struct Foo : public AcceptsFoo<Foo> // will check only when complete
{
int a;
int b;
};
// Foo is now complete, and that's also fine!
static_assert(IsFoo<Foo::IsFooType>);
struct FooBar : public AcceptsFoo<FooBar> // will fail once it is complete
{
//int a;
int b;
};
// will fail here
static_assert(IsFoo<FooBar::IsFooType>);
这也适用于主要编译器:https://godbolt.org/z/e1Gc4Kj5n