16

(やや) 古い記事decltypeでは、SFINAE と一緒に使用して、型が==orなどの特定の演算子をサポートしているかどうかを検出する方法を探っています<

<クラスが演算子をサポートしているかどうかを検出するコードの例を次に示します。

template <class T>
struct supports_less_than
{
    static auto less_than_test(const T* t) -> decltype(*t < *t, char(0))
    { }

    static std::array<char, 2> less_than_test(...) { }

    static const bool value = (sizeof(less_than_test((T*)0)) == 1);
};

int main()
{
    std::cout << std::boolalpha << supports_less_than<std::string>::value << endl;
}

trueもちろん演算子をstd::stringサポートしているので、これは を出力し<ます。ただし、演​​算子をサポートしていないクラスで使用しようとすると、コンパイラ エラーが発生します。<

error: no match for ‘operator<’ in ‘* t < * t’

したがって、SFINAE はここでは機能しません。これを GCC 4.4 と GCC 4.6 で試したところ、どちらも同じ動作を示しました。では、この方法で SFINAE を使用して、型が特定の式をサポートしているかどうかを検出することは可能ですか?

4

5 に答える 5

19

C++11 では、私が見つけた最短で最も一般的な解決策は次のとおりです。

#include <type_traits>

template<class T, class = decltype(std::declval<T>() < std::declval<T>() )> 
std::true_type  supports_less_than_test(const T&);
std::false_type supports_less_than_test(...);

template<class T> using supports_less_than = decltype(supports_less_than_test(std::declval<T>()));

#include<iostream>
struct random_type{};
int main(){
    std::cout << supports_less_than<double>::value << std::endl; // prints '1'
    std::cout << supports_less_than<int>::value << std::endl; // prints '1'
    std::cout << supports_less_than<random_type>::value << std::endl; // prints '0'
}

g++ 4.8.1および_clang++ 3.3


任意の演算子のより一般的な解決策 (UPDATE 2014)

std::less(binary) やstd::negate(unary)などの STD 演算子ラッパーを介して、すべての組み込み演算子にもアクセスできる (場合によっては特殊化されている) という事実を利用する、より一般的なソリューションがあります。

template<class F, class... T, typename = decltype(std::declval<F>()(std::declval<T>()...))> 
std::true_type  supports_test(const F&, const T&...);
std::false_type supports_test(...);

template<class> struct supports;
template<class F, class... T> struct supports<F(T...)> 
: decltype(supports_test(std::declval<F>(), std::declval<T>()...)){};

これは非常に一般的な方法で使用できます。特に C++14 では、型推定が演算子ラッパーの呼び出し (「透過的な演算子」) まで遅れる場合に使用できます。

二項演算子の場合、次のように使用できます。

#include<iostream>
struct random_type{};
int main(){
    std::cout << supports<std::less<>(double, double)>::value << std::endl; // '1'
    std::cout << supports<std::less<>(int, int)>::value << std::endl; // '1'
    std::cout << supports<std::less<>(random_type, random_type)>::value << std::endl; // '0'
}

単項演算子の場合:

#include<iostream>
struct random_type{};
int main(){
    std::cout << supports<std::negate<>(double)>::value << std::endl; // '1'
    std::cout << supports<std::negate<>(int)>::value << std::endl; // '1'
    std::cout << supports<std::negate<>(random_type)>::value << std::endl; // '0'
}

(C++11 標準ライブラリでは、C++14 の標準である、C++11 で手動で透明な演算子を実装できますdecltype(std::less<random_type>()(...))random_type)

構文は非常にスムーズです。このようなものが標準に採用されることを望みます。


2 つの拡張子:

1) raw 関数アプリケーションを検出するために機能します。

struct random_type{};
random_type fun(random_type x){return x;}
int main(){
    std::cout << supports<decltype(&fun)(double)>::value << std::endl; // '0'
    std::cout << supports<decltype(&fun)(int)>::value << std::endl; // '0'
    std::cout << supports<decltype(&fun)(random_type)>::value << std::endl; // '1'
}

2) 結果が特定の型に変換可能/比較可能かどうかをさらに検出できます。この場合double < doubleはサポートされていますが、結果が指定されたものではないため、コンパイル時に false が返されます。

std::cout << supports<std::equal_to<>(std::result_of<std::less<>(double, double)>::type, random_type)>::value << std::endl; // '0'

注: http://melpon.org/wandbox/で C++14 を使用してコードをコンパイルしようとしましたが、うまくいきませんでした。std::less<>その実装(clang ++ 3.5 c ++ 14)では、透過的な演算子( など)に問題があると思いますless<>。自動推定で独自に実装するとうまく機能するからです。

于 2013-09-04T00:48:46.303 に答える
9

SFINAE は Substitution Failure Is Not An Error の略であり、コードで選択に失敗する可能性のあるテンプレート関数がないため、less_than_test 関数をテンプレートにする必要があります。

template <class T>
struct supports_less_than
{
    template <class U>
    static auto less_than_test(const U* u) -> decltype(*u < *u, char(0))
    { }

    static std::array<char, 2> less_than_test(...) { }

    static const bool value = (sizeof(less_than_test((T*)0)) == 1);
};

int main()
{
    std::cout << std::boolalpha << supports_less_than<std::string>::value << endl;
}
于 2011-04-30T04:12:51.943 に答える
6

これはC++0xであり、sizeofベースのトリックはもう必要ありません...;-]

#include <type_traits>
#include <utility>

namespace supports
{
    namespace details
    {
        struct return_t { };
    }

    template<typename T>
    details::return_t operator <(T const&, T const&);

    template<typename T>
    struct less_than : std::integral_constant<
        bool,
        !std::is_same<
            decltype(std::declval<T const&>() < std::declval<T const&>()),
            details::return_t
        >::value
    > { };
}

(これはiammilindの回答に基づいていますが、Toperator<return-typeが異なるサイズである必要はなく、デフォルトで構築可能であるlong long必要もありません。)T

于 2011-04-30T17:56:35.047 に答える
3

以下の簡単なコードで要件が満たされます (コンパイル エラーが発生したくない場合)。

namespace supports {
  template<typename T>  // used if T doesn't have "operator <" associated
  const long long operator < (const T&, const T&);

  template <class T>
  struct less_than {
    T t;
    static const bool value = (sizeof(t < t) != sizeof(long long));
  };  
}

使用法:

supports::less_than<std::string>::value ====> true;  // ok
supports::less_than<Other>::value ====> false;  // ok: no error

[注: ないクラスのコンパイル エラーが必要な場合はoperator <、非常に少ないコード行で非常に簡単に生成できます。]

于 2011-04-30T05:04:11.513 に答える
0

@xDD は確かに正しいですが、彼の例は少し間違っています。

これはideoneでコンパイルされます:

#include <array>
#include <iostream>

struct Support {}; bool operator<(Support,Support) { return false; }
struct DoesNotSupport{};

template <class T>
struct supports_less_than
{
  template <typename U>
  static auto less_than_test(const U* u) -> decltype(*u < *u, char(0))
  { }

  static std::array<char, 2> less_than_test(...) { }

  static const bool value = (sizeof(less_than_test((T*)0)) == 1);
};

int main()
{
  std::cout << std::boolalpha << supports_less_than<Support>::value << std::endl;
  std::cout << std::boolalpha <<
     supports_less_than<DoesNotSupport>::value << std::endl;
}

結果は次のようになります。

true
false

こちら実際にご覧ください。

ポイントは、SFINAE はテンプレート関数にのみ適用されるということです。

于 2011-04-30T10:06:45.377 に答える