如何正确处理此SFINAE?

时间:2014-02-22 00:46:14

标签: c++ c++11 sfinae

我遇到了根据参数包的特性启用/禁用重载的问题。这是我想要回答的另一个问题。我有一个静态go()函数,如果所有Mixins类型都有静态check()方法,则应调用该函数。同样,如果go()具有Mixins方法,则应该调用另一个静态check()方法。我尝试创建基于此策略启用的重载,但出于某种原因,当我调用它时,它会选择第二次重载,尝试调用handle()

template<class T, class...>
using first = T;

template<class... Mixins>
struct Checker
{
public:
    template<class =
             first<void,
                   typename std::enable_if<has_check<Mixins>::value>::type...>>
    static void go()
    {
        auto x{ (Mixins::check(), 0)... };
        (void)x;
    }

    static void go()
    {
        auto x{ (Mixins::handle(), 0)... };
        (void)x;
    }
};

struct A { static void check() { std::cout << "check() "; } };
struct B { static void check() { std::cout << "check() "; } };

int main()
{
    Checker<A, B>::go();
}

Here is a demo.

我收到错误消息,指出A没有名为handle()的成员。这一直困扰着我,但我无法找到解决办法。我该如何解决这个问题?如何根据参数包正确协调重载决策?

1 个答案:

答案 0 :(得分:1)

你没有什么可以区分go的两种风格。当两者都可行时,重载决策将更喜欢非模板goThe ... vs. int technique works

template<class... Mixins>
struct Checker
{
private:
    template<class =
             first<void,
                   typename std::enable_if<has_check<Mixins>::value>::type...>>
    static void foo(int)
    {
        auto x{ (Mixins::check(), 0)... };
        (void)x;
    }

    static void foo(...)
    {
        auto x{ (Mixins::handle(), 0)... };
        (void)x;
    }

public:
    static void go() {
        foo(0);
    }
};

由于foo的两个重载都适用于呼叫foo(0),但foo(int)是一个比foo(...)更好的匹配,因此重载决策将更喜欢它SFINAE out。

编辑:我在这里犯了一个错误,我认为

template<class =
         first<void,
               typename std::enable_if<has_check<Mixins>::value>::type...>>

正确SFINAE远离foo的重载。但是,此处的错误不是替换失败,因为未命名的函数模板参数的默认值不依赖于函数模板的参数。通常,当您在类型T上参数化类并且希望基于f的某些属性约束成员函数T时,可以使用{{1的别名强制该依赖项}}:

T

然而,相同的技巧在OP的情况下不起作用,因为OP的类在包上参数化,并且无法为参数包提供默认值。我们必须更加棘手:

template <class T>
struct foo {
  template <class U=T,
            class=typename std::enable_if<std::is_integral<U>::value>::type>
  void f();
};

强制template<class T = void, class = first<T, typename std::enable_if<has_check<Mixins>::value, T>::type...>> static void foo(int) { auto x{ (Mixins::check(), 0)... }; (void)x; } first<...>展开对SFINAE有效。 See it at Coliru.