簡體   English   中英

可以根據不完整類型檢查概念嗎

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM