概念のラムディフィケーションは改善ですか、それとも悪い習慣ですか?

Aug 20 2020

概念にラムダを入れて、そこにコードを書くことができるようです。これを例として取り上げましょう。私はそのような概念の標準的な概念を好み、これはこの例の目的のためだけであることを覚えておいてください-godbolt

template<class T>
concept labdified_concept =
    requires {
            [](){                 
                T t, tt; // default constructible
                T ttt{t}; // copy constructible
                tt = t; //copy assignable
                tt = std::move(t); // move assignable
            };
        };

の代わりに:

template<class T>
concept normal_concept = 
    std::default_initializable<T> && std::movable<T> && std::copy_constructible<T>;

子羊化は改善ですか、それとも悪い習慣ですか?読みやすさの点からも。

回答

2 Barry Aug 21 2020 at 01:10

これは有効ではありません。未評価のコンテキストへのラムダの許可のポイントは、ステートメントでSFINAEを突然許可することではありませんでした。

[temp.deduct] / 9には、これを明確にするいくつかの文言があります。

ラムダ式関数型またはテンプレートパラメータに登場は、テンプレート引数控除の目的のために即時の文脈の一部とは見なされません。[:目的は、任意のステートメントを含む置換の失敗に対処するための実装の要求を回避することです。[

template <class T>
  auto f(T) -> decltype([]() { T::invalid; } ());
void f(...);
f(0);               // error: invalid expression not part of the immediate context

template <class T, std::size_t = sizeof([]() { T::invalid; })>
  void g(T);
void g(...);
g(0);               // error: invalid expression not part of the immediate context

template <class T>
  auto h(T) -> decltype([x = T::invalid]() { });
void h(...);
h(0);               // error: invalid expression not part of the immediate context

template <class T>
  auto i(T) -> decltype([]() -> typename T::invalid { });
void i(...);
i(0);               // error: invalid expression not part of the immediate context

template <class T>
  auto j(T t) -> decltype([](auto x) -> decltype(x.invalid) { } (t));   // #1
void j(...);                                                            // #2
j(0);               // deduction fails on #1, calls #2

終了例] —終了メモ]

要件に相当するものがないだけです。gccの動作は本当にあなたが期待するものです:

template <typename T> concept C = requires { []{ T t; }; };
struct X { X(int); };
static_assert(!C<X>); // ill-formed

ラムダの本体は直接のコンテキストの外にあるため、置換の失敗ではなく、ハードエラーです。

5 NicolBolas Aug 20 2020 at 21:58

このメカニズムで明らかに可読性の欠陥を無視して、それが実際にはありません仕事します。次のことを考慮してください。

template<labdified_concept T>
void foo(T t) {}

template<typename T>
void foo(T t) {}

概念のルールは、与えられたTものがを満たさない場合labdified_conceptfoo代わりに他のものをインスタンス化する必要があることを示しています。しかし、そのSSようなテンプレートを提供した場合、それは起こりません。代わりに、labdified_concept<SS>インスタンス化できないため、ハードエラーが発生します。

requires式内のものには特別な処理があり、特定のタイプのエラーを要件を満たすための失敗と見なすことができます。ただし、その処理はラムダの本体には適用されません。そこでは、不正な形式のコードが不正な形式であるため、インスタンス化しようとするとコンパイルエラーが発生します。

そして、それが機能したとしても、それはまだ機能しません。概念には、概念の包含に関する複雑なルールがあります。これにより、さまざまな概念を他の概念よりも高度に専門化されていると見なすことができます。これにより、さまざまな概念のオーバーロードが可能になり、より制約のある概念を呼び出すことができます。たとえば、必要なだけの概念default_initializabledefault_initializable、とを必要とする概念よりも一般的ですmoveable。したがって、タイプが両方を満たす場合、より制約があるため、後者が採用されます。

ただし、これはconceptsの特別なルールのためにのみ機能します。ラムダで要件を非表示にすると、これは機能しません。