58

operator==(メンバーまたは非メンバー関数)の存在をチェックする例を作成しようとしています。クラスにメンバーがあるかどうかを確認するのoperator==は簡単ですが、非メンバーがあるかどうかを確認するにはどうすればよいoperator==ですか?

これは私がこれまでにやらなければならないことです:

#include <iostream>

struct A
{
    int  a;

    #if 0
    bool operator==( const A& rhs ) const
    {
        return ( a==rhs.a);
    }
    #endif
};
#if 1
bool operator==( const A &l,const A &r )
{
    return ( l.a==r.a);
}
#endif


template < typename T >
struct opEqualExists
{
    struct yes{ char a[1]; };
    struct no { char a[2]; };

    template <typename C> static yes test( typeof(&C::operator==) );
    //template <typename C> static yes test( ???? );
    template <typename C> static no test(...);

    enum { value = (sizeof(test<T>(0)) == sizeof(yes)) };
};

int main()
{
    std::cout<<(int)opEqualExists<A>::value<<std::endl;
}

非メンバーの存在をテストするテスト関数を書くことは可能operator==ですか?はいの場合、どのように?

ところで、私は同様の質問をチェックしましたが、適切な解決策が見つかりませんでした:
SFINAE /テンプレートを使用してオペレーターが存在するかどうかをチェックすることは可能ですか?

これは私が試したものです:

template <typename C> static yes test( const C*,bool(*)(const C&,constC&) = &operator== );

ただし、非メンバーのoperator ==が削除されると、コンパイルは失敗します

4

13 に答える 13

47

C ++ 03

次のトリックが機能し、そのようなすべてのオペレーターに使用できます。

namespace CHECK
{
  class No { bool b[2]; };
  template<typename T, typename Arg> No operator== (const T&, const Arg&);

  bool Check (...);
  No& Check (const No&);

  template <typename T, typename Arg = T>
  struct EqualExists
  {
    enum { value = (sizeof(Check(*(T*)(0) == *(Arg*)(0))) != sizeof(No)) };
  };  
}

使用法:

CHECK::EqualExists<A>::value;

2つ目は、それ自体とは似ていない、などのtemplate typename Arg特殊なケースに役立ちます。このような場合の使用法は次のとおりです。A::operator==(short)class

CHECK::EqualExists<A, short>::value
//                    ^^^^^ argument of `operator==`

デモ


C ++ 11

とを持ってsizeofいる場合、null参照トリックを使用する必要はありませんdecltypestd::declval

namespace CHECK
{
  struct No {}; 
  template<typename T, typename Arg> No operator== (const T&, const Arg&);

  template<typename T, typename Arg = T>
  struct EqualExists
  {
    enum { value = !std::is_same<decltype(std::declval<T>() < std::declval<Arg>()), No>::value };
  };  
}

デモ

于 2011-06-30T14:17:11.570 に答える
16

Boostのコンセプトチェックライブラリ(BCCL)http://www.boost.org/doc/libs/1_46_1/libs/concept_check/concept_check.htmをご覧ください

これにより、プログラムをコンパイルするためにクラスが一致しなければならない要件を記述できます。あなたはあなたがチェックできるもので比較的自由です。たとえばoperator==、クラスFooの存在を確認すると、次のようになります。

#include <boost/concept_check.hpp>


template <class T>
struct opEqualExists;

class Foo {
public:
    bool operator==(const Foo& f) {
       return true;
    }

   bool operator!=(const Foo& f) {
      return !(*this == f);
   }

   // friend bool operator==(const Foo&, const Foo&);
   // friend bool operator!=(const Foo&, const Foo&);
};

template <class T>
struct opEqualExists {
   T a;
   T b;

   // concept requirements  
   BOOST_CONCEPT_USAGE(opEqualExists) {
      a == b;
   }
};


/*
bool operator==(const Foo& a, const Foo& b) {
   return true; // or whatever
}
*/


/*
bool operator!=(const Foo& a, const Foo& b) {
   return ! (a == b); // or whatever
}
*/


int main() {
   // no need to declare foo for interface to be checked

   // declare that class Foo models the opEqualExists concept
   //   BOOST_CONCEPT_ASSERT((opEqualExists<Foo>));
   BOOST_CONCEPT_ASSERT((boost::EqualityComparable<Foo>)); // need operator!= too
}

このコードは、の2つの実装のいずれかが使用可能である限り、正常にコンパイルされoperator==ます。

@MatthieuM.と@LucTourailleのアドバイスに従って、boost::EqualityComparable使用例を提供するためにコードスニペットを更新しました。繰り返しになりますが、EqualityComparableでは宣言も強制されることに注意してくださいoperator!=

于 2011-06-30T12:44:45.503 に答える
12

メンバーの存在を確認するために、c++11タイプの特性のみを使用することも可能です。

#include <type_traits>
#include <utility>

template<class T, class EqualTo>
struct has_operator_equal_impl
{
    template<class U, class V>
    static auto test(U*) -> decltype(std::declval<U>() == std::declval<V>());
    template<typename, typename>
    static auto test(...) -> std::false_type;

    using type = typename std::is_same<bool, decltype(test<T, EqualTo>(0))>::type;
};

template<class T, class EqualTo = T>
struct has_operator_equal : has_operator_equal_impl<T, EqualTo>::type {};

この特性は次のように使用できます。

bool test = has_operator_equal<MyClass>::value;

結果のタイプはまたは(のエイリアスから継承するため)のhas_operator_equalいずれかになり、どちらもブール値である静的メンバーを定義します。std::true_typestd::false_typestd::is_same::typevalue


クラスがを定義しているかどうかをテストできるようにするoperator==(someOtherType)場合は、2番目のテンプレート引数を設定できます。

bool test = has_operator_equal<MyClass, long>::value;

ここで、テンプレートパラメータMyClassは、の存在をテストしているクラスでありoperator==long比較できるようにするタイプです。たとえば、をMyClass持っているテストを行いますoperator==(long)

(最初の例のようEqualToに)指定しないままにすると、デフォルトでT、になり、通常の定義になりoperator==(MyClass)ます。

注意事項:の場合のこの特性は、、または暗黙的に変換可能な任意の値、たとえば、などoperator==(long)に当てはまります。longlongdoubleint


内にあるものを置き換えるだけで、他の演算子や関数のチェックを定義することもできますdecltype。を確認するには!=、単に置き換えます

static auto test(U*) -> decltype(std::declval<U>() == std::declval<V>());

static auto test(U*) -> decltype(std::declval<U>() != std::declval<V>());
于 2016-02-04T17:34:39.747 に答える
8

c ++ 14の時点では、標準のバイナリ関数が大多数の演算子のほとんどの作業を実行します。

#include <utility>
#include <iostream>
#include <string>
#include <algorithm>
#include <cassert>


template<class X, class Y, class Op>
struct op_valid_impl
{
    template<class U, class L, class R>
    static auto test(int) -> decltype(std::declval<U>()(std::declval<L>(), std::declval<R>()),
                                      void(), std::true_type());

    template<class U, class L, class R>
    static auto test(...) -> std::false_type;

    using type = decltype(test<Op, X, Y>(0));

};

template<class X, class Y, class Op> using op_valid = typename op_valid_impl<X, Y, Op>::type;

namespace notstd {

    struct left_shift {

        template <class L, class R>
        constexpr auto operator()(L&& l, R&& r) const
        noexcept(noexcept(std::forward<L>(l) << std::forward<R>(r)))
        -> decltype(std::forward<L>(l) << std::forward<R>(r))
        {
            return std::forward<L>(l) << std::forward<R>(r);
        }
    };

    struct right_shift {

        template <class L, class R>
        constexpr auto operator()(L&& l, R&& r) const
        noexcept(noexcept(std::forward<L>(l) >> std::forward<R>(r)))
        -> decltype(std::forward<L>(l) >> std::forward<R>(r))
        {
            return std::forward<L>(l) >> std::forward<R>(r);
        }
    };

}

template<class X, class Y> using has_equality = op_valid<X, Y, std::equal_to<>>;
template<class X, class Y> using has_inequality = op_valid<X, Y, std::not_equal_to<>>;
template<class X, class Y> using has_less_than = op_valid<X, Y, std::less<>>;
template<class X, class Y> using has_less_equal = op_valid<X, Y, std::less_equal<>>;
template<class X, class Y> using has_greater_than = op_valid<X, Y, std::greater<>>;
template<class X, class Y> using has_greater_equal = op_valid<X, Y, std::greater_equal<>>;
template<class X, class Y> using has_bit_xor = op_valid<X, Y, std::bit_xor<>>;
template<class X, class Y> using has_bit_or = op_valid<X, Y, std::bit_or<>>;
template<class X, class Y> using has_left_shift = op_valid<X, Y, notstd::left_shift>;
template<class X, class Y> using has_right_shift = op_valid<X, Y, notstd::right_shift>;

int main()
{
    assert(( has_equality<int, int>() ));
    assert((not has_equality<std::string&, int const&>()()));
    assert((has_equality<std::string&, std::string const&>()()));
    assert(( has_inequality<int, int>() ));
    assert(( has_less_than<int, int>() ));
    assert(( has_greater_than<int, int>() ));
    assert(( has_left_shift<std::ostream&, int>() ));
    assert(( has_left_shift<std::ostream&, int&>() ));
    assert(( has_left_shift<std::ostream&, int const&>() ));

    assert((not has_right_shift<std::istream&, int>()()));
    assert((has_right_shift<std::istream&, int&>()()));
    assert((not has_right_shift<std::istream&, int const&>()()));
}
于 2016-09-06T11:46:13.263 に答える
6

C ++ 20

ユーザーが提供するテンプレートタイプに等式演算子があるかどうかを確認したいと思います。その場合は、概念が役立ちます。

#include<concepts>

struct S{
   int x;
};

template<class T>
requires std::EqualityComparable<T>
void do_magic(T a, T b){
    return a == b;
}

int main(){
    // do_magic(S{}, S{}); Compile time error
    do_magic(56, 46); // Okay int has == and !=

return 0;
}

を持たない型を渡して==オーバーロードすると!=、コンパイラは次のメッセージでエラーになります。

EqualityComparableタイプによって満たされない概念


std::EqualityComparableWith<T, U>また、2つの異なるタイプ間の過負荷をチェックできるコンセプトを使用することもできます。

Incrementableなどの標準に追加された概念は他にもたくさんあります。こちらをご覧ください

于 2019-08-04T17:44:06.633 に答える
3

私はこの質問が長い間答えられていることを知っていますが、Boostがtype_traitsライブラリにたくさんの「hasoperator」特性を追加しただけで、将来この質問を見つけた人にとっては注目に値するかもしれません。その中にはhas_equal_toがあります。 OPが求めていたことを実行します。

于 2012-01-12T02:58:14.283 に答える
3

この質問はすでに数回回答されていますが、演算子と一緒にoperator==使用することで、他の操作の存在または基本的に他の操作(たとえば、特定の名前のメンバー関数のテスト)を確認する簡単な方法があります。decltype,

namespace detail
{
    template<typename L, typename R>
    struct has_operator_equals_impl
    {
        template<typename T = L, typename U = R> // template parameters here to enable SFINAE
        static auto test(T &&t, U &&u) -> decltype(t == u, void(), std::true_type{});
        static auto test(...) -> std::false_type;
        using type = decltype(test(std::declval<L>(), std::declval<R>()));
    };
} // namespace detail

template<typename L, typename R = L>
struct has_operator_equals : detail::has_operator_equals_impl<L, R>::type {};

これと同じアプローチを使用して、型に特定の引数リストで呼び出しT可能なメンバー関数があるかどうかを確認できます。foo

namespace detail
{
    template<typename T, typename ...Args>
    struct has_member_foo_impl
    {
        template<typename T_ = T>
        static auto test(T_ &&t, Args &&...args) -> decltype(t.foo(std::forward<Args>(args)...), void(), std::true_type{});
        static auto test(...) -> std::false_type;
        using type = decltype(test(std::declval<T>(), std::declval<Args>()...));
    };
} // namespace detail

template<typename T, typename ...Args>
struct has_member_foo : detail::has_member_foo_impl<T, Args...>::type {};

これにより、コードの意図がはるかに明確になると思います。それに加えて、これはC ++ 11ソリューションであるため、新しいC++14またはC++17の機能に依存しません。もちろん、最終的な結果は同じですが、これは、これらの種類のものをテストするための私の好みのイディオムになりました。

編集:オーバーロードされたコンマ演算子の非常識なケースを修正しました。私はいつもそれを見逃しています。

于 2018-05-31T19:54:59.810 に答える
1

次の形式のメタ関数を考えてみましょう。これは==、指定されたタイプの等式演算子(つまり)の存在をチェックします。

template<typename T>
struct equality { .... };

ただし、一部のコーナーケースではそれだけでは不十分な場合があります。たとえば、クラスXが定義しoperator==ているが、を返さずbool、代わりにを返すとしますY。したがって、この場合、何をequality<X>::value返す必要がありますか? trueまたはfalse?ええと、それは私たちが今知らない特定のユースケースに依存します、そしてそれは何かを仮定してそれをユーザーに強制するのは良い考えではないようです。ただし、一般に、戻り型はであると想定できるboolため、インターフェース自体でこれを表現しましょう。

template<typename T, typename R = bool>
struct equality { .... };

のデフォルト値Rbool、これが一般的なケースであることを示しています。のリターンタイプoperator==が異なる場合、たとえばY、次のように言うことができます。

equality<X, Y>  //return type = Y

指定されたリターンタイプもチェックします。デフォルトでは、

equality<X>   //return type = bool

このメタ関数の実装の1つを次に示します。

namespace details
{
    template <typename T, typename R, typename = R>
    struct equality : std::false_type {};

    template <typename T, typename R>
    struct equality<T,R,decltype(std::declval<T>()==std::declval<T>())> 
       : std::true_type {};
}

template<typename T, typename R = bool>
struct equality : details::equality<T, R> {};

テスト:

struct A  {};
struct B  {  bool operator == (B const &); };
struct C  {  short operator == (C const &); };

int main()
{
    std::cout<< "equality<A>::value = " << equality<A>::value << std::endl;
    std::cout<< "equality<B>::value = " << equality<B>::value << std::endl;
    std::cout<< "equality<C>::value = " << equality<C>::value << std::endl;
    std::cout<< "equality<B,short>::value = " << equality<B,short>::value << std::endl;
    std::cout<< "equality<C,short>::value = " << equality<C,short>::value << std::endl;
}

出力:

equality<A>::value = 0
equality<B>::value = 1
equality<C>::value = 0
equality<B,short>::value = 0
equality<C,short>::value = 1

オンラインデモ

お役に立てば幸いです。

于 2016-04-01T15:52:14.377 に答える
1

リチャードホッジスゴッドボルトのc++17わずかに変更されたバージョン

#include <functional>
#include <type_traits>

template<class T, class R, class ... Args>
std::is_convertible<std::invoke_result_t<T, Args...>, R> is_invokable_test(int);

template<class T, class R, class ... Args>
std::false_type is_invokable_test(...);

template<class T, class R, class ... Args>
using is_invokable = decltype(is_invokable_test<T, R, Args...>(0));

template<class T, class R, class ... Args>
constexpr auto is_invokable_v = is_invokable<T, R, Args...>::value;

template<class L, class R = L>
using has_equality = is_invokable<std::equal_to<>, bool, L, R>;
template<class L, class R = L>
constexpr auto has_equality_v = has_equality<L, R>::value;

struct L{};

int operator ==(int, L&&);

static_assert(has_equality_v<int>);
static_assert(!has_equality_v<L>);
static_assert(!has_equality_v<L, int>);
static_assert(has_equality_v<int, L>);
于 2018-02-27T08:47:31.117 に答える
1

std::equal_to<Type>二項演算子(または他の二項ファンクター)をテストする場合は、(または他のオーバーロードされた構造体メンバー)を使用して、より一般的なソリューションを作成できます。

struct No {};

template<class T, class BinaryOperator>
struct ExistsBinaryOperator>
{
    enum { value = !std::is_same<decltype(std::declval<BinaryOperator>()(std::declval<T>(), std::declval<T>())), No>::value };
};

使用法:

using Type = int;
constexpr bool hasEqual = ExistsBinaryOperator<Type, std::equal_to<Type>>::value;
于 2020-08-21T10:20:38.613 に答える
0

IMO、これはクラスのプライベート属性を処理するため、クラス自体の一部である必要があります。テンプレートはコンパイル時に解釈されます。デフォルトではoperator==、同じタイプのオブジェクトに対してビット単位のコピー(浅いコピー)またはビット単位の比較を行う、コンストラクタ、デストラクタ、およびコピーコンストラクタを生成します。特殊なケース(異なるタイプ)はオーバーロードする必要があります。グローバル演算子関数を使用する場合は、プライベート部分にアクセスするためのフレンドとして関数を宣言する必要があります。そうでない場合は、必要なインターフェイスを公開する必要があります。時々これは本当に醜いので、関数の不必要な公開を引き起こすかもしれません。

于 2011-06-30T13:47:27.157 に答える
0

参考までに、operator==存在する かどうかを確認する必要なしに、問題をどのように解決したかを投稿しています。

#include <iostream>
#include <cstring>

struct A
{
    int  a;
    char b;

    #if 0
    bool operator==( const A& r ) const
    {
        std::cout<<"calling member function"<<std::endl;

        return ( ( a==r.a ) && ( b==r.b ) );
    }
    #endif
};
#if 1
bool operator==( const A &l,const A &r )
{
    std::cout<<"calling NON-member function"<<std::endl;
    return ( ( l.a==r.a ) &&( l.b==r.b ) );
}
#endif

namespace details
{
struct anyType
{
    template < class S >
    anyType( const S &s ) :
        p(&s),
        sz(sizeof(s))
    {
    }

    const void *p;
    int sz;
};
bool operator==( const anyType &l, const anyType &r )
{
    std::cout<<"anyType::operator=="<<std::endl;
    return ( 0 == std::memcmp( l.p, r.p, l.sz ) );
}
} // namespace details

int main()
{
    A a1;
    a1.a=3;a1.b=0x12;
    A a2;
    a2.a=3;a2.b=0x12;

    using details::operator==;

    std::cout<< std::boolalpha << "numbers are equals : " << ( a1 == a2 ) <<std::endl;
}
于 2011-07-01T07:30:41.840 に答える
0

@ coder3101の回答に加えて、concepts必要な関数存在テストを実装するのに役立ちます。たとえばstd::equality_comparable、次のシナリオをチェックする4つの簡単なテストを使用して実装されます。

Aおよび変数についてBは、次の式が有効であることを確認してください。

A == B, returns bool
A != B, returns bool
B == A, returns bool
B != A, returns bool

コンパイル時にそれらのいずれかが不正である場合、プログラムはコンパイルされません。このテストの実装(標準から簡略化):

template <typename T> concept equality_comparable
    = requires(T t, T u) {
        { t == u } -> std::convertible_to<bool>;
        { t != u } -> std::convertible_to<bool>;
        { u == t } -> std::convertible_to<bool>;
        { u != t } -> std::convertible_to<bool>;
    };

ご覧のとおり、このコンセプトをカスタマイズして、条件に合った独自のコンセプトを作成できます。たとえば、の存在のみを強制したい場合はoperator==、次のようにすることができます。

template <typename T> concept my_equality_comparable
    = requires(T t, T u) {
        { t == u } -> std::convertible_to<bool>;
        { u == t } -> std::convertible_to<bool>;
    };

詳細についてconceptsは、C++20をご覧ください。

于 2020-07-10T15:52:31.650 に答える