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)) };
};

我在第二行得到“错误:'class Node'没有名为'setInstanceKey'的成员”,即使应该执行else子句。

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

有没有办法使这项工作?

谢谢。

4

1 回答 1

3

仅仅因为从未输入过 if 分支并不意味着分支中的代码可能是无效的。(另一种思考方式:您不能保证任何关于优化的事情,但您的代码只有在死分支优化时才有效。)

你所做的是将分支转移到一个函数。通常你有一个这样的框架:

// 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 回答