【问题标题】:how to use enable_if with overloads如何将 enable_if 与重载一起使用
【发布时间】:2016-08-12 00:55:17
【问题描述】:
enum class enabler{};

template<typename T> 
class X {
   template<typename std::enable_if<std::is_class<T>::value,enabler>::type = enabler()>
    void func();
    void func(int a);
    void func(std::string b);
};

我有这个类,这 3 个重载用于 func。我需要第二个/第三个版本可用于类/非类类型,而第一个版本仅可用于类类型。当我尝试像上面那样使用enable_if 时,非类类型的类实例化会产生编译错误。

【问题讨论】:

  • SFINAE 仅适用于 deduced 类型。
  • 您能详细说明一下吗?
  • 您实际上并不需要使用 sfinae。在您的情况下,static_assert 就足够了。请参阅我的(迟到的)答案中的最小示例。
  • @skypjack 是的,如果这不是一个简化的例子。在非简化版本中,它可能会影响其他函数的重载解析。

标签: c++ templates c++11 sfinae enable-if


【解决方案1】:

要使SFINAE 起作用,必须推导出模板参数。在您的情况下,T 在您尝试实例化 func 时已经知道,因此如果 enable_if 条件是 false,而不是 SFINAE,则会出现硬错误。

要修复错误,只需添加一个默认值为T 的模板参数,并在enable_if 检查中使用这个新参数。现在扣除发生了,SFINAE 可以为非类类型启动。

template<typename U = T,
         typename std::enable_if<std::is_class<U>::value,enabler>::type = enabler()>
void func();

而且你也不需要专门的enabler 类型,这也可以

template<typename U = T,
         typename std::enable_if<std::is_class<U>::value, int>::type* = nullptr>
void func();

【讨论】:

  • 这种方法的缺点是您可以使用显式模板参数运行func():即使T 不是类,func&lt;std::string&gt;() 也会导致类重载
  • @AndreiR。是的,如果你想在脚上开枪,你总能找到一些方法来做到这一点。
  • @Praetorian 你仍然可以使用像template&lt;typename..., typename U = T, typename std::enable_if&lt;std::is_class&lt;U&gt;::value, int&gt;::type* = nullptr&gt; void func(); 这样的保护参数包。显式模板参数将被淘汰。
  • @mkmostafa 我拒绝了编辑,因为我认为您试图防止人们明确指定 func 的模板参数,并且该编辑不能解决问题。这样做又意味着没有扣除,SFINAE 不会发生,当条件为假时你会得到一个硬错误。
  • @skypjack 当然,这有点好,但仍然令人惊讶,因为用户提供的模板参数没有效果。我认为应该记录像上面示例这样的函数,以警告用户不要指定模板参数,然后让他们做他们想做的事。
【解决方案2】:

我不太确定您在此处使用 enabler 的目的是什么,但是您不能做您正在尝试的事情,因为您的成员函数的声明必须有效,因为 T 不是由func。为了达到你想要的添加额外重载的目的,你可以使用一些适度设计的继承。

struct XBaseImpl {
  // whatever you want in both versions
  void func(int a) { }
  void func(std::string b) { }
};

template <typename, bool> struct XBase;

// is_class is true, contains the extra overload you want
template <typename T>
struct XBase<T, true> : XBaseImpl {
  static_assert(std::is_class<T>{}, "");  // just to be safe
  using XBaseImpl::func;
  void func() { }  // class-only
};

// is_class is false
template <typename T>
struct XBase<T, false> : XBaseImpl { };

template<typename T>
class X : public XBase<T, std::is_class<T>{}> { };

【讨论】:

    【解决方案3】:

    您没有启用或禁用某些功能。
    您只需要在一种特定情况下出现编译时错误。
    因此,您不需要依赖 sfinae,static_assert 就足够了。

    作为一个最小的工作示例:

    #include<string>
    
    template<typename T> 
    class X {
    public:
        void func() {
            static_assert(std::is_class<T>::value, "!");
            // do whatever you want here
        }
    
        void func(int a) {}
        void func(std::string b) {}
    };
    
    int main() {
        X<int> x1;
        X<std::string> x2;
    
        x2.func(42);
        x2.func();
    
        x1.func(42);
        // compilation error
        // x1.func();
    }
    

    有一次 SO 用户对我说:这不是 sfinae,这是 - 替换失败始终是错误 - 在这种情况下,您应该改用 static_assert
    他是对的,如上例所示,static_assert 比 sfinae 更容易编写和理解,并且也能正常工作。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-11-20
      • 2014-08-30
      • 2014-01-09
      • 1970-01-01
      • 1970-01-01
      • 2021-12-14
      • 2014-09-12
      相关资源
      最近更新 更多