可以针对不完整类型检查概念吗
Can a concept be checked against incomplete type
我偶然发现了这个:
#include <type_traits>
#include <concepts>
template<class T>
concept IsFoo = requires(T a)
{
{a.a} -> std::same_as<int>;
};
#if 1
// Will not compile, because Foo currently has incomplete type
template<IsFoo AFoo>
struct AcceptsFoo
{};
#else
template<class AFoo>
struct AcceptsFoo
{};
#endif
struct Foo
{
int a;
int b;
AcceptsFoo<Foo> obj;
};
https://gcc.godbolt.org/z/j43s4z
其他变体 (crtp) https://gcc.godbolt.org/z/GoWfhq
Foo 不完整,因为它必须实例化 AcceptsFoo
,但要这样做,Foo
必须完整,否则无法检查 IsFoo
。这是 GCC 中的错误,还是标准这么说?后者会令人难过,因为这会阻止概念与一些众所周知的模式(例如 CRTP)一起使用。
我注意到 clang 确实给出了类似的错误:https://gcc.godbolt.org/z/d5bEez
您可以检查一个概念是否符合不完整的类型——但如果该概念检查实际上需要对需要它完成的类型做任何事情,那么概念检查将失败.
即使在那里,您也必须小心,因为允许(并且将)实现缓存概念检查以更快地编译 - 所以如果您尝试 C<T>
而 T
不完整并重试当 T
完成时,那些应该给出不同的答案,你是在自找麻烦。
Foo
在您检查它时还不完整,因此它自然没有名为 a
的成员。这个真的不行。
because this prevents concepts for being used together with some well-known patterns such as CRTP.
这样一起使用,可以。与使用 CRTP 一样,您也无法直接从传递到基 class 的模板参数访问任何内容,您始终必须小心延迟该类型的任何实例化,直到它完成。
这归根结底是同一个道理:
template <typename Derived>
struct B {
typename Derived::type x;
};
struct D : B<D> {
using type = int;
};
无效。
[编辑] 这在 g++ 10.3、11.2 和当前的 clang 上按预期工作。一些评论表明这是未定义的行为,因此请注意未来编译器可能发生的意外更改。
原解:
我也偶然发现了这一点,并通过允许类型不完整或具有所需约束的完整,成功地使我的 CRTP with concepts 工作。
除了定义的 IsFoo
,我还定义了 IsComplete
辅助函数(使用 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
),我希望它也能在其他编译器上工作。
[编辑] 正如评论中指出的那样,它接受任何不完整的类型,但这是有意的。只有外部静态断言会过滤完整的类型案例。感谢@David Herring 在 Bar 上的示例,我在这里写它进行测试:https://godbolt.org/z/sqc75qqMv
[EDIT2] 现在处理 CRTP 变体,没有任何未定义的行为,也没有 IsComplete 解决方法,只需存储类型以在 class 完成后进行检查。
// 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
我偶然发现了这个:
#include <type_traits>
#include <concepts>
template<class T>
concept IsFoo = requires(T a)
{
{a.a} -> std::same_as<int>;
};
#if 1
// Will not compile, because Foo currently has incomplete type
template<IsFoo AFoo>
struct AcceptsFoo
{};
#else
template<class AFoo>
struct AcceptsFoo
{};
#endif
struct Foo
{
int a;
int b;
AcceptsFoo<Foo> obj;
};
https://gcc.godbolt.org/z/j43s4z
其他变体 (crtp) https://gcc.godbolt.org/z/GoWfhq
Foo 不完整,因为它必须实例化 AcceptsFoo
,但要这样做,Foo
必须完整,否则无法检查 IsFoo
。这是 GCC 中的错误,还是标准这么说?后者会令人难过,因为这会阻止概念与一些众所周知的模式(例如 CRTP)一起使用。
我注意到 clang 确实给出了类似的错误:https://gcc.godbolt.org/z/d5bEez
您可以检查一个概念是否符合不完整的类型——但如果该概念检查实际上需要对需要它完成的类型做任何事情,那么概念检查将失败.
即使在那里,您也必须小心,因为允许(并且将)实现缓存概念检查以更快地编译 - 所以如果您尝试 C<T>
而 T
不完整并重试当 T
完成时,那些应该给出不同的答案,你是在自找麻烦。
Foo
在您检查它时还不完整,因此它自然没有名为 a
的成员。这个真的不行。
because this prevents concepts for being used together with some well-known patterns such as CRTP.
这样一起使用,可以。与使用 CRTP 一样,您也无法直接从传递到基 class 的模板参数访问任何内容,您始终必须小心延迟该类型的任何实例化,直到它完成。
这归根结底是同一个道理:
template <typename Derived>
struct B {
typename Derived::type x;
};
struct D : B<D> {
using type = int;
};
无效。
[编辑] 这在 g++ 10.3、11.2 和当前的 clang 上按预期工作。一些评论表明这是未定义的行为,因此请注意未来编译器可能发生的意外更改。
原解:
我也偶然发现了这一点,并通过允许类型不完整或具有所需约束的完整,成功地使我的 CRTP with concepts 工作。
除了定义的 IsFoo
,我还定义了 IsComplete
辅助函数(使用 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
),我希望它也能在其他编译器上工作。
[编辑] 正如评论中指出的那样,它接受任何不完整的类型,但这是有意的。只有外部静态断言会过滤完整的类型案例。感谢@David Herring 在 Bar 上的示例,我在这里写它进行测试:https://godbolt.org/z/sqc75qqMv
[EDIT2] 现在处理 CRTP 变体,没有任何未定义的行为,也没有 IsComplete 解决方法,只需存储类型以在 class 完成后进行检查。
// 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