SFINAE with boost enable if



我正在尝试实现一个模板化的对类,该类使用sfinae来区分数组和非数组类型。到目前为止,我有以下代码:

template <typename T>
class pair
{
public:
   //default constructors. one for non-array types and one for arrays. These work.
   template <typename temp_type = T>
   pair(typename boost::disable_if<boost::is_array<temp_type>>::type* ignore = 0)
      : first(T())
      , second(T())
   {}
   template <typename temp_type = T>
   pair(typename boost::enable_if<boost::is_array<temp_type>>::type* ignore = 0)
   {}
   //assignment operator attempts for non-array types (not in code at same time.) These don't work.
   template<typename temp_type = T> 
   pair<temp_type>& operator=(pair<typename boost::disable_if<boost::is_array<temp_type>>::type>                const& rhs)
   {
       this->first = rhs.first;
       this->second = rhs.second;
       return *this;
   }
   template<typename temp_type = T> 
   auto operator=(pair<temp_type> const& rhs) -> pair<typename boost::disable_if<boost::is_array<temp_type>>::type>&
   {
       this->first = rhs.first;
       this->second = rhs.second;
       return *this;
   }
   T first;
   T second;
};

第一次尝试赋值运算符失败,出现"非法使用类型void"错误。第二个编译,但当我调试时,MSVC告诉我"没有可执行代码与该行关联。"我使用的是MSVC 12 64位编译器。

如果你能对这里的错误提供任何见解,那将非常有帮助。

此外,我对c++中的sfinae做了一些观察(这可能是对的,也可能是错的):

  • sfinae要求对成员函数进行模板化,但不能为此目的使用类模板类型。为什么
  • sfinae可以通过只修改模板参数而不修改返回类型或输入来完成。这到底是怎么回事。这种方法的灵活性有多大

我知道这是长篇大论,并引用了许多其他帖子中涉及的主题,但我无法将这些解释放在一起,以简洁地解释c++中的sfinae。

我读过的一些帖子:

向非C++程序员解释C++SFINAE(高级介绍)

使用enable_if(构造函数的sfinae)选择类构造函数

谢谢你的帮助。

编辑:

我已经根据下面的注释修改了我的代码,但我仍然无法使其按预期工作(编译器甚至看不到源代码。)这是一个有趣的情况,因为这个例子完全是人为设计的,默认赋值运算符实际上在这种情况下工作。尽管如此,我认为编译器不应该覆盖我重载运算符的尝试。

我尝试了以下4种方法,但它们似乎都没有内置到可执行文件中:

template<typename temp_type = T> 
pair<temp_type>& operator=(pair<typename boost::disable_if<boost::is_array<temp_type>, temp_type>::type> const& rhs)
{
        this->first = rhs.first;
        this->second = rhs.second;
        return *this;
}
template<typename temp_type = T>
auto operator=(pair<temp_type> const& rhs) -> pair<typename boost::disable_if<boost::is_array<temp_type>, temp_type>::type>&
{
    this->first = rhs.first;
    this->second = rhs.second;
    return *this;
}
template<typename ret_type = boost::disable_if<boost::is_array<T>, T>::type, typename = void>
pair<ret_type>& operator=(pair<ret_type> const& rhs)
{
    this->first = rhs.first;
    this->second = rhs.second;
    return *this;
}
template<typename temp_type = T, typename boost::enable_if<boost::is_array<temp_type>, temp_type>::type = 0>
pair<T>& operator=(pair<temp_type> const& rhs)
{
    this->first = rhs.first;
    this->second = rhs.second;
    return *this;
}

想法?

别忘了,转换赋值可以通过模板函数实现,但复制和移动赋值运算符不能。

用户声明的复制赋值运算符X::operator=是类X的非静态非模板成员函数,仅具有一个类型为XX&const X&volatile X&const volatile X&的参数。

如果类定义没有显式声明复制赋值运算符,则隐式声明一个。如果类定义声明了移动构造函数或移动赋值运算符,则隐式声明的复制赋值运算符被定义为已删除;否则,它被定义为默认

用户声明的移动赋值运算符X::operator=是类X的非静态非模板成员函数,仅具有一个类型为X&&const X&&volatile X&&const volatile X&&的参数。

和纸币

因为模板赋值运算符或采用右值引用参数的赋值运算符从来都不是复制赋值运算符,所以这种赋值运算符的存在不会抑制复制赋值运算符的隐式声明。此类赋值操作符与其他赋值操作符(包括复制赋值操作符)一起参与重载解析,如果选中,则将用于指定对象。

(上述引用见第12.8节,草案n3936中的措辞)

@BenVoigt的回答解释了为什么你的尝试失败了,这里只是一个替代解决方案。您可以根据模板实例化的类型将operator=调用分派到适当的重载:

#include <iostream>
#include <boost/type_traits.hpp>
template <typename T>
class pair
{    
public:    
    pair& operator=(pair const& rhs)
    {
        return assign(rhs, boost::is_array<T>());
    }
private:
    pair& assign(pair const&, boost::true_type)
    {
        std::cout << "array" << std::endl;
        return *this;
    }
    pair& assign(pair const&, boost::false_type)
    {
        std::cout << "not array" << std::endl;
        return *this;
    }
};
int main()
{
    pair<int> a, b;
    b = a;
    pair<int[]> c, d;
    c = d;
}

输出:

not array
array

您也可以对构造函数执行同样的操作,将(C++11)调用委托给另一个:

pair() : pair(boost::is_array<T>()) {}
pair(boost::true_type) { /*initialize array pair*/ }
pair(boost::false_type) { /*initialize non-array pair*/ }

代码看起来更干净,您不必与上的编译器竞争,后者的operator=更符合实际参数

DEMO

最新更新