[英]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
您可以根據不完整的類型檢查概念 - 但如果該概念檢查實際上需要對需要它完成的類型執行任何操作,那么概念檢查將失敗。
即使在那里,您也必須小心,因為允許(並且將)緩存概念檢查以更快地編譯 - 因此,如果您在T
不完整時嘗試C<T>
並在T
完成時重試,並且這些應該給出不同的答案,你是在自找麻煩。
Foo
在您檢查它時並不完整,因此它自然沒有名為a
的成員。 這真的不可能工作。
因為這會阻止概念與一些眾所周知的模式(例如 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 與概念一起工作。
除了定義的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
)上運行良好,我希望它也適用於其他編譯器。
[編輯] 正如評論中所指出的,這接受任何不完整的類型,但這是有意的。 只有外部 static 斷言將過濾完整的類型案例。 感謝@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
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.