【问题标题】:enable_if: minimal example for void member function with no argumentsenable_if:无参数的 void 成员函数的最小示例
【发布时间】:2017-01-16 00:28:02
【问题描述】:

我试图更好地理解 C++11 中的 std::enable_if,并一直在尝试编写一个最小的示例:一个类 A 具有一个成员函数 void foo(),它根据类型具有不同的实现T 来自类模板。
下面的代码给出了预期的结果,但我还没有完全理解它。为什么版本V2 可以工作,而V1 不行?为什么需要“冗余”类型U

#include <iostream>
#include <type_traits>

template <typename T>
class A {

    public:

        A(T x) : a_(x) {}

        // Enable this function if T == int
        /* V1 */ // template <           typename std::enable_if<std::is_same<T,int>::value,int>::type = 0>
        /* V2 */ template <typename U=T, typename std::enable_if<std::is_same<U,int>::value,int>::type = 0>
        void foo() { std::cout << "\nINT: " << a_ << "\n"; }

        // Enable this function if T == double
        template <typename U=T, typename std::enable_if<std::is_same<U,double>::value,int>::type = 0>
        void foo() { std::cout << "\nDOUBLE: " << a_ << "\n"; }

    private:

        T a_;

};

int main() {
    A<int> aInt(1); aInt.foo();
    A<double> aDouble(3.14); aDouble.foo();
    return 0;
}

是否有更好的方法来实现所需的结果,即基于类模板参数对 void foo() 函数进行不同的实现?

【问题讨论】:

  • 您的示例不适合使用 enable_if。简单的重载可以解决你的问题。 enable_if 主要用于 deduced 模板参数。
  • 使用std::enable_if 将适合于将推导的浮点 类型与integral 类型分开。像这样的两种特定类型更适合重载
  • @KerrekSB @WhozCraig 在这种特殊情况下我将如何超载?通过使用类外定义void A&lt;int&gt;::foo() {}void A&lt;double&gt;::foo() {}?我的意图是最终代码仅包含所需的函数版本(即,如果从未调用过这样的函数,则没有函数 A&lt;double&gt;::foo()
  • 据我了解,问题是关于为什么以及如何冗余的 typename U=T 使示例工作,但并非没有它。有人解释一下吗?

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


【解决方案1】:

我知道这不能完全回答你的问题,但它可能会给你一些关于如何使用std::enable_if 的想法和理解。

您可以将 foo 成员函数替换为以下内容并具有相同的功能:

template<typename U=T> typename std::enable_if<std::is_same<U,int>::value>::type
foo(){ /* enabled when T is type int */ }

template<typename U=T> typename std::enable_if<std::is_same<U,double>::value>::type
foo(){ /* enabled when T is type double */ }

不久前,我对 enable_if 的工作原理有了很好的了解,但遗憾的是,我忘记了它的大部分复杂性,只记得更实用的使用方法。

【讨论】:

  • 我认为您还需要删除函数之前的void,因为它现在已经包含在std::enable_if&lt;&gt; 中。但是感谢这个输入,帮助我更接近了解正在发生的事情。你会说这是实现编译时多态性的合法方式吗?
  • @untergam 哎呀,不错!我现在将对其进行编辑。我认为这是一种合法的做事方式,我只是不确定是否有更好的方法来做到这一点
【解决方案2】:

至于第一个问题:为什么 V1 不起作用? SFINAE 仅适用于重载解决方案 - 但是 V1 在实例化类型 A 时会导致错误,远早于 foo() 重载解决方案。

我想有很多可能的实现 - 最合适的实现取决于所讨论的实际案例。一种常见的方法是将A 中因不同模板类型而不同的部分推迟到帮助器类中。

template <typename T>
class A_Helper;

template <>
class A_Helper<int> {
public:
    static void foo( int value ){
        std::cout << "INT: " << value << std::endl;
    }
};

template <>
class A_Helper<double> {
public:
    static void foo( double value ){
        std::cout << "DOUBLE: " << value << std::endl;
    }
};

template <typename T>
class A {
public:

    A( T a ) : a_(a) 
    {}

    void foo(){
        A_Helper<T>::foo(a_);
    }

private:
    T a_;
};

A 的其余部分只能以通用方式声明一次 - 只有不同的部分被推迟到帮助程序。有很多可能的变化 - 取决于您的要求......

【讨论】:

  • 感谢@j_kubik 的回答。我看到了这个带有模板化帮助类的解决方案是如何工作的,但这正是我一开始就试图避免的那种代码膨胀。我更喜欢enable_if 或至少具有类似短语法的最小版本。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-11-11
  • 2017-01-27
  • 2012-01-22
  • 1970-01-01
  • 1970-01-01
  • 2012-10-01
  • 1970-01-01
相关资源
最近更新 更多