不特定の型をC ++ 20で使用するにはどうすればよいですか?

Aug 16 2020

型に引数を取る特定のメソッドがあるという要件を表現するためにC ++ 20の概念を書き込もうとしていますが、この概念の目的上、引数の型が何であるかは関係ありません。

私は次のようなものを書こうとしました:

template <typename T>
concept HasFooMethod = requires(T t, auto x)
{
    { t.Foo(x) } -> std::same_as<void>;
};

ただし、gccとclangの両方がこれを拒否し、この方法でrequires式のパラメーターリストで「auto」を使用できないというエラーが発生します。

別の方法は、「x」の型を2番目のテンプレートパラメーターとして配置することです。

template <typename T, typename TX>
concept HasFooMethod = requires(T t, TX x)
{
    { t.Foo(x) } -> std::same_as<void>;
};

ただし、これには、概念を使用するたびにTXを明示的に指定する必要があり、推測することはできません。

struct S { void Foo(int); };
static_assert(HasFooMethod<S>);         // doesn't compile
static_assert(HasFooMethod<S, int>);    // the 'int' must be specified

Fooが不特定の型の引数を取ることを可能にする概念を書く方法はありますか?

質問制約されたテンプレートのメンバ関数を必要とする概念定義は、同じ非常に似ていますが、ない:その質問は、(テンプレート)メソッドが取ることができることを要求する方法を尋ねる任意のこの質問はメソッドが取ることを要求についてですが、与えられた概念を満たすタイプをいくつかの特定の型、種類が指定されていないことであるが。数量詞に関して、もう1つの質問は、(制限された)全称記号について尋ねることですが、これは存在記号についてです。他の質問の答えも私の場合には当てはまりません。

回答

4 NicolBolas Aug 17 2020 at 13:48

概念は、あなたが探している種類の機能を提供することを意図したものではありません。したがって、彼らはそれを提供しません。

概念は、テンプレートを制約し、テンプレートがその定義で使用する(または少なくとも自由に使用できる)一連の式またはステートメントを指定することを目的としています。

非常に制約しているテンプレート内で、式を作成するとt.Foo(x)、のタイプがわかりますx。これは、具象型、テンプレートパラメータ、またはテンプレートパラメータから派生した名前のいずれかです。いずれにせよ、のタイプはx制約されているテンプレートで利用できます。

したがって、そのようなテンプレートを制約する場合は、のタイプtとのタイプの両方を使用しますx。その時点で両方を使用できるため、このような制約を作成しても問題はありません。つまり、制約はT分離型としてオンになりません。との間の関連付けにTありXます。

概念は、制約の実際の使用場所との関連性がないため、真空中で機能することを意図したものではありません。ユーザーがstatic_assertそれらに対してクラスを実行できるように、単項概念の作成に集中するべきではありません。概念は、型がそれらを満たすかどうかをテストするためのものではありません(これは基本的にあなたstatic_assertがしていることです)。それらは、それらを使用するテンプレート定義を制約するためのものです。

制約はFooCallableWith、ではなく、である必要がありますHasFooMethod

NathanReed Aug 17 2020 at 16:30

これに近いことは、(ほとんど)何にでも暗黙的に変換できるアダプタタイプを定義することで実現できます。

struct anything
{
    // having both these conversions allows Foo's argument to be either
    // a value, an lvalue reference, or an rvalue reference

    template <typename T>
    operator T&();

    template <typename T>
    operator T&&();
};

これらの演算子は、評価されていないコンテキストでのみ使用されるため、実装する必要がないことに注意してください(実際、すべてのタイプTに実装できるわけではありません)。

次に、次のHasFooMethodように書くことができます。

template <typename T>
concept HasFooMethod = requires(T t, anything a)
{
    { t.Foo(a) } -> std::same_as<void>;
};