【问题标题】:Can I use a SFINAE test in a control flow statement?我可以在控制流语句中使用 SFINAE 测试吗?
【发布时间】:2010-08-12 22:48:29
【问题描述】:

我有一个 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)) };
};

我在第二行得到“错误:‘类节点’没有名为‘setInstanceKey’的成员”,即使应该执行 else 子句。

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

有没有办法让它工作?

谢谢。

【问题讨论】:

  • Ew,相等测试左侧的常量。 :)
  • 我在这里捡到的。它们被称为尤达条件。 stackoverflow.com/questions/2349378/…
  • @Kevin:你的意思是你以前没有? D:快,还原!
  • 从字面上看,你想让你的代码读起来像尤达语吗?
  • 另外,! 的优先级高于 ==,因此您将 value 与 1 进行比较。

标签: c++ templates g++ sfinae


【解决方案1】:

仅仅因为从未输入过 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>());
}

【讨论】:

  • 它不太工作。 g++ 无法区分“void foo(const T& pX, true_tag)”和“void foo(const T&, false_tag)”。我收到了之前定义的错误。
  • @Kevin:除非true_tagfalse_tag 是同一类型,否则这是不可能的。你把它们打错了吗?
  • @GMan:在每个 foo() 上方的行上添加“模板 ”可修复“先前定义的”错误。不知道当我测试它时我是否应该期望它工作......现在仍在调试其他错误。
  • @Kevin:哦,对不起!这完全是我的错,微不足道的错误。函数需要是模板才能获得T!我已经用 cmets 更新了代码,更好地匹配了你的使用,并给了你一些关于实现元函数的提示。
  • @GMan:谢谢。在另一个 SO 线程上有一个关于是否使用枚举或静态变量作为值的参数。看起来并没有真正解决。你切换的原因是什么?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-12-30
  • 2017-05-07
  • 1970-01-01
  • 1970-01-01
  • 2015-11-07
  • 1970-01-01
相关资源
最近更新 更多