4

VS7.1 で記述されたコードを見つけたので、MacOSX で動作させようとしています。以下のコード スニペットは、 SFINAEの原則に関するものだと理解しています。私が理解していることから、コードは、コンパイル時にテンプレートのインスタンス化マジックに依存することで、何かの型を知るために使用されます。つまり、適切なオーバーロードは、テンプレート引数を確認することで選択されます。

これが私が持っているコードです。問題のみを表示するためにやや単純化されています。

template <typename T>
struct SomeClass
{
};

template <>
struct SomeClass<char>
{
    typedef char Type;
};

template <typename T>
struct IsChar
{
    typedef char Yes;
    typedef int No;

    template <typename U>
    static Yes Select(U*, typename SomeClass<U>::Type* p = 0);
    template <typename U>
    static No Select(U*, ...);
    static T* MakeT();

    const static bool Value = sizeof(Select(MakeT())) == sizeof(Yes);
};

私は単にこれを次のように使用しています:

if (IsChar<int>::Value)
{
    ...

上記のコードをコンパイルすると、int を使用するときに Type の typedef が欠落しているため、適切に動作し、最上位のクラスが選択されます。

代わりに char を使用すると...

if (IsChar<char>::Value)
{
    ...

...コンパイラは、どちらを使用するかわからないため、あいまいな Select 関数について文句を言います。私が読んだことから、オーバーロードの解決では、省略記号パラメーターが優先されません (...)。したがって、最初のものを選択することを知っている必要があります。

コードは少なくとも VS7.1 では正常に動作していましたが、MacOSX の gcc や Linux の gcc4.4 では動作しませんでした。

これを修正する方法はありますか?多分それは通常別の方法で行われますか?

ありがとう!

更新: 私が提供したサンプル コードが少し単純化しすぎていることに気付きました。誤ってそのように見せたとしても、ここで型をチェックしているとは思えないからです。ここにコードがないので、今夜はもう少し情報を集める必要があります。そのために残念。

UPDATE2: 元のコードに慣れていないか、このようにテンプレートを使用していることが原因で、私のプレゼンテーションが悪い場合でも。その間、もう少し情報を掘り下げます。これらの構造が何らかの理由で X にあり、私が指定した名前がすべて間違っていると仮定しましょう。コンパイラの問題はどうですか? ここで適切なオーバーロードされた関数を選択できないのはなぜですか? これは私も興味深いです。私が言ったように、私は全体的な目的が何であるかをよりよく説明して戻ってきます.

編集

元のコードを詳しく見てみると、ここで提案されているように、boost::integral_constant と boost::enable_if を使用しています。問題は、テンプレートの引数がどのように推定されるかに特有のものであり、セットアップされた方法では機能しませんでした。ただし、ゲオルクが回答の最後に提案したことに従って、物事を修正して受け入れることができました。私は今、次のものを持っています:

typedef char Yes;
typedef int No;

template <typename U> static Yes Select(typename SomeClass<U>::Type* p);
template <typename U> static No Select(...);

static const bool Value = sizeof(Select<T>(0)) == sizeof(Yes);

これはうまくいきます。少し実験していると、Select 関数に 2 つの関数パラメーターがあると問題が発生することがわかりました。理由はわかりません。物事をよりよく理解したら、これに戻ります。

ご助力いただきありがとうございます。少なくとも、ここでの原則と、物事がどのように機能するかを理解しています。まだ不明な詳細のみ。

4

2 に答える 2

9

意図を誤解していない限り、上記の使用例では SFINAE を使用する必要はありません。のタイプのみを静的にアサートしたい場合は、次のTypeようなものを使用できます。

template<class T1, class T2> struct SameType {
    static const bool Value = false;
};

template<class T> struct SameType<T, T> {
    static const bool Value = true;
};

template <typename T>
struct IsChar {
    static const bool Value = SameType<T, char>::Value;
};

これを SFINAE に本当に使用する必要がある場合 (つまり、テンプレート パラメーターに基づいて関数を無効/有効にする)、上記をBoostsenable_ifのようなものと組み合わせて使用​​するだけです。

template<class T> 
typename boost::enable_if_c<IsChar<T>::Value, void>::type
someFunction() {
}

または、Boost を最後まで使用できる場合:

template<class T> 
typename boost::enable_if<boost::mpl::is_same<T, char>, void>::type
someFunction() {
}

更新

SomeClassこれを読み直して、 の特殊化に typedef があるかどうかを実際に確認したい場合は、ここTypeからソリューションを使用できるはずです。

template<class T> struct HasType {
    template<class U> static char (&test(typename U::Type const*))[1];
    template<class U> static char (&test(...))[2];
    static const bool Value = (sizeof(test< SomeClass<T> >(0)) == 1);
};

その場合IsChar、確かに誤称ですが、HasTypeまたはのようなものHasTypedefTypeはより説明的です:)

于 2011-02-02T09:17:48.470 に答える
6

私のコメントでは、通常、これらの述語の結果を の値として使用しないと述べましたif。これが理由です。

// assume we have has_typedef_type from the Wikipedia page:

template <typename T>
void foo(const T& x)
{
    if (has_typedef_type<T>::value)
    {
        // the predicate is true, so T::type exists
        typename T::type y = x;
    }
    else
    {
        // the predicate is false, so T::type doesn't exist, do something else
        float y = x;
    }
}

これは問題ないように見えるかもしれませんが、述語が false の場合を考慮してください。コンパイラはこれをコンパイルしようとします。

// let's say we called it with double
void foo<double>(const double& x)
{
    if (false)
    {
        // wait, double doesn't have this!
        typename double::type y = x;
    }
    else
    {
        float y = x;
    }
}

問題は、コードがデッドコード除去で除去されるとしても、コードの形式が正しくないことです。解決策は、ifコンパイル時も作成することですが、最初にいくつかの定型文を作成します。

// in C++0x, these are defined in <type_traits>, but we'll do it ourselves
// (Boost has these as well)
typename <typename T, T Value>
struct integral_constant
{
    typedef T type;
    static const T value = Value;
};

typedef integral_constant<bool, true> true_type;
typedef integral_constant<bool, false> false_type;

邪魔にならないように、関数を定義します。

namespace detail
{
    // here are the real implementations
    template <typename T>
    void foo(const T& x, true_type)
    {
        // the predicate is true, so T::type exists
        typename T::type y = x;
    }

    template <typename T>
    void foo(const T& x, false_type)
    {
        // the predicate is false, so T::type doesn't exist, do something else
        float y = x;
    }
}

template <typename T>
void foo(const T& x)
{
    detail::foo(x, // chose which function to call, using the type of this:
                integral_constant<bool, has_typedef_type<T>::value>());
}

ブランチは互いに完全に独立しているため、これですべて問題ありません。

于 2011-02-02T09:36:20.810 に答える