2

C++で次のようなことをする方法はありますか

template<typename TAnimal>
bool can_eat(TAnimal& animal) where bool TAnimal::IsAlive() exists
{
    return !animal.IsAlive();
}

//...

Duck duck;
assert(can_eat(duck) == true); //compiles

Wood wood;
can_eat(wood); // fails to compile because wood doesn't have IsAlive()

私の意見では、明示的なインターフェイスは、関数が期待するものをより明確にします。ただし、実際のインターフェイス クラスを作成したくありません (非常に面倒です)。

4

6 に答える 6

9

要件を強制するために使用しないでください。enable_ifを使用enable_ifすると、関数が「消える」ため、ユーザーは非常に混乱する可能性があります。典型的な症状は、のようなエラーメッセージです。これでは、要件が違反していることをユーザーに正確に伝えることはできません。error: no matching function for call to expression

static_assertC++0x を想定して、代わりに を使用して要件を適用する必要があります。C++03 を使用している場合、static_assert(Boost などのSTATIC_ASSERT) エミュレーションを使用する必要があるかどうかは、通常、1 つのエラー メッセージを別のエラー メッセージと交換することを意味するため、トスアップです。

対比:

// SFINAE for types that do not decay to int
template<
    typename T
    , typename = typename std::enable_if<
        std::is_same<
            typename std::decay<T>::type
            , int
        >::value
    >::type
>
void
f(T&&)
{}

// using static assert instead
template<
    typename T
>
void
g(T&&)
{
    static_assert( std::is_same<typename std::decay<T>::type, int>::value
                 , "Constraints violation" );
}

GCC を使用すると、次のエラーが表示されf("violation")ます (両方のメッセージにファイル名と行番号が付いています)。

error: no matching function for call to 'f(const char [10])'

一方、g("violation")利回り:

error: static assertion failed: "Constraints violation"

foo: parameter type must be CopyConstructibleここで、テンプレート内などのアサーションで明確で明示的なメッセージを使用することを想像してくださいfoo

そうは言っても、 SFINAE とstatic_assertは多少敵対的であるため、明示的な制約違反メッセージと巧妙なオーバーロードの両方を持つことは、常に可能または簡単であるとは限りません。


やりたいことは、Boost.ConceptCheckを使用して簡単に実現できます。ただし、アウトオブライン コードである Constraint クラスを記述する必要があります。また、利用可能な場所で使用するとは思わないstatic_assertため、エラーメッセージはそれほど良くないかもしれません. これは将来変更される可能性があります。

もう 1 つの可能性は、static_assert+ 型の特性を使用することです。このアプローチの興味深い点は、C++0x のライブラリには一連の有用な特性が付属していることです。これらの特性は、行外のコードを記述せずにすぐに使用できます。さらに興味深いのは、トレイトの使用は制約の記述に限定されず、巧妙なオーバーロードを作成するために SFINAE と共に使用することもできることです。

ただし、おそらく C++ が関数の名前を処理する方法が原因で、型が操作の特定のメンバーをサポートしているかどうかをチェックするためにすぐに利用できる特性はありません。has_member<T, &T::member_to_test_for>テスト対象のメンバーがそもそも存在する場合にのみ意味があるため、どちらのようなものも使用できません(オーバーロードのようなものや、メンバーの署名をトレイトに渡す必要があるという事実は無視してください)。

任意の式をトレイトに変換する方法は次のとおりです。

template<typename T>
struct void_ {
    typedef void type;
};

template<typename T>
struct trait {
private:
    typedef char yes[1];
    typedef char no[2];

    template<typename U>
    static
    yes&
    test(U&&
        , typename void_<decltype( std::declval<U&>().member() )>::type* = 0);

    static
    no&
    test(...);

public:
    static constexpr bool value = sizeof test(std::declval<T>()) == sizeof(yes);
};

これがいかに大きいかに注意してください。Boost.ConceptCheck 制約クラスを作成する方が簡単かもしれません (ただし、SFINAE では再利用できないことに注意してください)。

任意の式はstd::declval<U&>().member()です。ここでの要件は、左辺値参照が与えられた場合U(または、必要に応じTて特性が true の場合)、それに対する呼び出しmember()が有効であることです。

また、その式の型 (つまり、この式に対して選択されたオーバーロードの結果の型) が型に変換可能であることを確認することもできます (それmemberその型であるかどうかを確認しないでください。正当な理由もなく制限が厳しすぎるためです)。ただし、これはトレイトを膨らませますが、これも制約クラスを優先します。

関数テンプレートの署名の一部を作成する方法はわかりませんがstatic_assert(これはあなたが望むもののようです)、クラス テンプレート内に表示できます。Boost.ConceptCheck もそれをサポートしていません。

于 2011-08-28T18:03:03.220 に答える
5

これは、概念が解決することを意図したものです。

概念は、最新の C++ 標準への提案された追加でしたが、委員会がそれらが言語に含めるのに十分しっかりしていると確信していなかったため、削除されました。Herb Sutter が標準からの除外について書いた内容を参照してください。

技術的には、テンプレートは使用できるものは何でも使用するだけなので、概念は必要ありません (つまり、where節をなくすと、求めているものが得られます)。必要なメソッドがコンパイル時に存在しない場合、コードはコンパイルされません。しかし、概念により、コーダーは型のインターフェイスをより明示的に制御できるようになり、現在ほとんどのコンパイラで提供されているよりもはるかに合理的なエラー メッセージが表示されます。

于 2011-08-28T16:57:27.803 に答える
2

このためのオファーBOOST_STATIC_ASSERTを増やします。最近承認されたバージョンの C++ 標準では、組み込みバージョンのstatic_assertマクロが提供されます。

enable_if一方、これにはあまり適していません。使用できますが、主な目的はenable_ifあいまいなオーバーロードを区別することです。

于 2011-08-28T17:41:24.780 に答える
1
where void TAnimal::IsAlive() exists

私はあなたが意味すると思いますbool TAnimal::IsAlive()か?もしそうなら、C++ はあなたが求めていることをすでに行っています。Duck に IsAlive() メソッドがある場合、これはコンパイルされます。

Duck duck;
assert(can_eat(duck) == true); //compiles

Wood に IsAlive() メソッドがない場合、これはコンパイルされません。

Wood wood;
can_eat(wood); // fails to compile because wood doesn't have IsAlive()

それはあなたが求めているものですよね?

于 2011-08-28T16:56:37.707 に答える
0

他の人が言ったように、これはうまくいくでしょう。関数が存在しない場合、テンプレートをインスタンス化することはできません。

ブースト ライブラリには、この種のことを支援するためのクラスがいくつか含まれています関連する型特性ライブラリもあります。これを使用して、呼び出したい関数が存在するかどうかをコンパイル時に判断できる場合があります。

私はこれを自分で使用したことがないことを認めなければなりませんが、あなたが望むものを達成するためにそれを使用できるはずだと私には思えます...

于 2011-08-28T17:06:52.597 に答える
0

何もする必要はありません。例から架空の「where ... exists」を省略すれば、通常の C++ コードで動作します。

ある条件下でのみ機能を利用できるようにすることを主張する場合は、ここからと組み合わせboost::enable_ifてみてhas_memberください: http://lists.boost.org/Archives/boost/2002/03/27229.php

いくつかの条件が満たされた場合にのみテンプレート関数のインスタンス化を許可するという考えです...しかし、SFINAE以来、条件が実際のコンパイルと同じである場合、コンパイラは基本的にすでにそれを行う予定です-関数の時間の必要性(あなたの例のように)。

于 2011-08-28T16:56:29.780 に答える