c++ - SFINAE:编译器不选择专门的模板类

标签 c++ templates sfinae

我有一个 SFINAE问题:

在下面的代码中,我希望 C++ 编译器选择专用仿函数并打印“special”,但它打印的是“general”。

#include <iostream>
#include <vector>

template<class T, class V = void>
struct Functor {
  void operator()() const {
    std::cerr << "general" << std::endl;
  }
};

template<class T>
struct Functor<T, typename T::Vec> {
  void operator()() const {
    std::cerr << "special" << std::endl;
  }
};

struct Foo {
  typedef std::vector<int> Vec;
};

int main() {
  Functor<Foo> ac;
  ac();
}

如何修复它以便自动使用专用结构?请注意,我不想直接将 Functor 结构专门化为 Foo,但我想将其专门化为具有 Vec 类型的所有类型.

附言:我使用的是 g++ 4.4.4

最佳答案

很抱歉在最后的回答中误导了你,我想了想这样会更简单。所以我会尝试在这里提供一个完整的解决方案。解决此类问题的一般方法是编写一个 traits 辅助模板,并将其与 enable_if(C++11、boost 或手动实现)一起使用来决定类特化:

特质

一个简单的方法,不一定是最好的,但写起来很简单:

template <typename T>
struct has_nested_Vec {
    typedef char yes;
    typedef char (&no)[2];
    template <typename U>
    static yes test( typename U::Vec* p );
    template <typename U>
    static no test( ... );

    static const bool value = sizeof( test<T>(0) ) == sizeof(yes);
};

方法很简单,提供两个模板函数,返回不同大小的类型。其中一个采用嵌套的 Vec 类型,另一个采用省略号。对于所有具有嵌套 Vec 的类型,第一个重载是更好的匹配(省略号是任何类型的最差匹配)。对于那些没有嵌套 Vec 的类型,SFINAE 将丢弃该重载,剩下的唯一选项将是省略号。所以现在我们有一个特征来询问是否有任何类型具有嵌套的 Vec 类型。

启用如果

你可以从任何库中使用这个,或者你可以自己动手,这很简单:

template <bool state, typename T = void>
struct enable_if {};

template <typename T>
struct enable_if<true,T> {
    typedef T type;
};

当第一个参数为 false 时,基本模板是唯一的选项,并且没有嵌套的 type,如果条件为 true,然后 enable_if 有一个我们可以与 SFINAE 一起使用的嵌套 type

实现

现在我们需要提供模板和专门化,将 SFINAE 仅用于那些具有嵌套 Vec 的类型:

template<class T, class V = void>
struct Functor {
    void operator()() const {
        std::cerr << "general" << std::endl;
    }
};
template<class T>
struct Functor<T, typename enable_if<has_nested_Vec<T>::value>::type > {
    void operator()() const {
        std::cerr << "special" << std::endl;
    }
};

每当我们用一个类型实例化 Functor 时,编译器将尝试使用特化,它会依次实例化 has_nested_Vec 并获得一个真值,传递给 启用_if。对于那些值为 false 的类型,enable_if 没有嵌套的 type 类型,因此特化将在 SFINAE 和将使用基本模板。

您的具体情况

在您的特定情况下,您似乎不需要专门化整个类型而只需要专门化运算符,您可以将这三个元素混合成一个:一个 Functor 调度到基于 Vec 存在的两个内部模板函数之一,消除了对 enable_if 和特征类的需要:

template <typename T>
class Functor {
   template <typename U>
   void op_impl( typename U::Vec* p ) const {
      std::cout << "specialized";
   }
   template <typename U>
   void op_impl( ... ) const {
      std::cout << "general";
   }
public:
   void operator()() const {
      op_impl<T>(0);
   }
};

关于c++ - SFINAE:编译器不选择专门的模板类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11320887/

相关文章:

c++ - 这个 A<B>::c ("d") 结构是什么意思?用模板命名空间?

c++ - 为类型集专门化函数模板

c++ - "CL.exe"未在独立项目中编译 C++

c++ - 使用 Boost.Asio 的广播问题

c++ - 如何提取和删除 std::string 中的字符

php - 如何在wordpress中动态分配页面模板

C++ 在模板中使用静态常量类成员

c++ - 使用 SFINAE 检查函数是否为 constexpr

c++ - 异常规范是 SFINAE 中直接上下文的一部分吗?

c++ - 此 C++ 实现模式的名称