2

クラスに関数があるかどうかを確認するための SFINAE テストがあります。テストは正しく動作しますが、if ステートメントで使用しようとするとコンパイル エラーが発生します。

//SFINAE test for setInstanceKey()
template <typename K>
class HasSetInstanceKey
{
    template <typename C>
    static char test( typeof(&C::setInstanceKey) );

    template <typename C>
    static long test(...);

public:
    enum { value = 1 == sizeof(test<K>(0)) };
};

else 節が実行されているはずなのに、2 行目に「エラー: 'class Node' には 'setInstanceKey' という名前のメンバーがありません」というメッセージが表示されます。

if ( 0 != HasSetInstanceKey<T>::value)
    instance->setInstanceKey(instanceKey);
else
    ...

これを機能させる方法はありますか?

ありがとう。

4

1 に答える 1

3

if-branch に入らないからといって、ブランチ内のコードが無効になる可能性があるわけではありません。(別の考え方: 最適化については何も保証されていませんが、コードはデッドブランチ最適化でのみ有効です。)

あなたがすることは、ブランチを関数にシフトすることです。通常、次のようなフレームワークがあります。

// holds some integral constant
template <typename T, T V>
struct integral_constant
{
    static const T value = V;
};

// holds a boolean constant
template <bool V>
struct bool_type : integral_constant<bool, V>
{};

typedef bool_type<true> true_type; // a true boolean constant
typedef bool_type<false> false_type; // a false boolean constant

typedef const true_type& true_tag; // tag a function as the true variant
typedef const false_type& false_tag; // tag a function as the false variant

次に、次のようなもの:

namespace detail
{
    template <typename T, typename KeyType>
    void foo(T* instance, const KeyType& instanceKey, true_tag)
    {
        // we are in the true variant, so our meta-function's value was true
        // therefore, instance has the ability to do setInstanceKey
        instance->setInstanceKey(instanceKey);
    }

    template <typename T, typename KeyType>
    void foo(T*, const KeyType&, false_tag)
    {
        // we are in the false variant, so our meta-function's value was false
        // therefore, instance does not have the right capabilities, 
        // so do nothing
    }
}

// interface, forwards to correct implementation function
template <typename T, typename KeyType>
void foo(T* instance, const KeyType& instanceKey)
{
    // pass instance, but to the overloaded foo 
    // that accepts the right boolean result
    detail::foo(instance, instanceKey, // plug the value into a bool_type, 
                bool_type<HasSetInstanceKey<T>::value>()); // and instantiate it
                // will either go into the true_tag or false_tag
}

bool_type使いやすくするために、正しい からメタ関数を継承する必要があることをお勧めします。

namespace detail
{
    // implementation
    template <typename K>
    class HasSetInstanceKey
    {
        // note, using char and long doesn't necessarily guarantee
        // they each have a unique size. do this instead:
        typedef char yes[1];
        typedef char no[2]; // these must have different sizes

        template <typename C>
        static yes& test( typeof(&C::setInstanceKey) );

        template <typename C>
        static no& test(...);

    public:
        // check against size of yes result
        static const bool value = sizeof(test<K>(0)) == sizeof(yes);
    };
}

template <typename K>
struct HasSetInstanceKey : // delegate to implementation, take result and
    bool_type<detail::HasSetInstanceKey<K>::value> // inherit from the 
                                                   // appropriate bool_type
{};

したがって、次のようになります。

template <typename T, typename KeyType>
void foo(T* instance, const KeyType& instanceKey)
{
    // because it inherits from bool_type, it can be implicitly
    // converted into either true_tag or false_tag
    detail::foo(instance, instanceKey, HasSetInstanceKey<T>());
}
于 2010-08-12T23:00:27.630 に答える