C++的enable_if_t和SFINAE

3
我正在尝试理解为什么这段代码没有按预期工作。
#include <cstdio>
#include <vector>
#include <type_traits>
using namespace std;

struct Foo {

};

template<typename T, typename = void>
void compare(const T&a, const T&b) {
    cout << "default" << endl;
}

template<typename T, std::enable_if_t<std::is_same<T, Foo>::value>>
void compare(const T& a, const T &b) {
    cout << "In object" << endl;
}

int main(int argc, char const *argv[]) {
    compare(1, 2);

    {
        vector<int> a, b;
        compare(a, b);
    }

    {
        Foo a, b;
        compare(a, b);
    }

    return 0;
}

在所有情况下,“default”都会被打印。对于最后一种情况,我期望第二个函数被调用。
3个回答

3
您没有专门化compare(函数模板无法部分专门化)。相反,您提供了一个重载。
而重载总是不合法的: 1. 要么enable_if_t未定义。 2. 或者它指定了一个类型为void的非类型模板参数。
因此,它永远不会被调用,因为SFINAE会放弃它,而选择顶部始终有效的重载。
对于函数模板来说,专门化通常都是错误的答案。相反,您应该委托给一个类模板,这将在真正的专门化时表现出预期的行为:
template<typename T, typename = void>
struct compare_impl { 
  static void execute(T const& l, T const& r) { /*Base case code*/ }
};

template<typename T>
struct compare_impl<T, std::enable_if_t<std::is_same<T, Foo>::value>> {
   static void execute(T const& l, T const& r) { /*Special case code*/ }
};

template<typename T>
void compare (T const& l, T const& r) { compare_impl<T>::execute(a, b); }

谢谢!我现在有点明白了 :) 在比较函数中,我们需要使用compare_impl<T>::,我猜。 - jack_carver
@jack_carver - 哎呀,我就知道我会漏掉什么东西。 - StoryTeller - Unslander Monica
你还需要在 compare 函数中将参数 (l, r) 切换为 (a, b) - Daniel H

2
使用类模板是一种有效的解决方案,这里还有另一种使用标签分派实现此结果的方法:
    template <class T>
    void compare(const T & l, const T & r, std::true_type)
    {
      cout << "In object" << endl;
    }

    template <class T>
    void compare(const T & l, const T & r, std::false_type)
    {
      cout << "default" << endl;
    }

    template <class T>
    void compare(const T & l, const T & r)
    {
      compare(l, r, std::is_same<T, Foo>{});
    }

1
将任务委托给专门的函数对象可以在参数类型、以及它们是rvalue还是lvalue方面提供很大的灵活性。
#include <iostream>
#include <vector>
#include <type_traits>
using namespace std;


// general case
template<class T> 
struct compare_impl
{
    template<class U, class V>
    auto operator()(U&& a, V&& b) const {
        cout << "default" << endl;
    }
};

// compare interface
template<class T, class U>
auto compare(T && a, U && b)
{
    using ctype = std::common_type_t<std::decay_t<T>, std::decay_t<U>>;
    auto impl = compare_impl<ctype>();
    return impl(a, b);
}


// now specialise for objects for which we want custom behaviour
struct Foo {

};

template<>
struct compare_impl<Foo>
{
    template<class U, class V>
    auto operator()(U&& a, V&& b) const {
        cout << "In object" << endl;
    }
};


int main(int argc, char const *argv[]) {

    compare(1, 2);

    {
        vector<int> a, b;
        compare(a, b);
    }

    {
        Foo a, b;
        compare(a, b);
    }

    return 0;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接