【问题标题】:C++ template specialization - unable to match function definitionC++ 模板专业化 - 无法匹配函数定义
【发布时间】:2020-06-19 02:38:49
【问题描述】:

一天半以来,我一直在苦苦挣扎,试图学习模板专业化和 std::enable_if()。我没有得到的东西。我试图根据编译时的类类型和用户规范来允许类方法的返回类型规范。例如,如果类类型是整数,我可能希望方法返回浮点类型(或整数)。

我的实际用例是一个数组,我对其中的所有值进行平均。我什至可能想要一个布尔返回值来反映二进制“投票”的结果。有很多组合。必须有一种方法可以做到这一点,而无需单独创建所有重载。我的示例代码是我能做到的最简单的。

如果有任何指点,我将不胜感激。如果确实是这样的话,我该如何进行这种部分专业化?

#include <string>

template <class T>
class Test {
public:
    Test(T val) { val_ = val; }
    template <typename U> U halve ();
private:
    T val_;
};


template<class T> template<typename U>
U Test<T>::halve() {
    throw std::invalid_argument("Cannot halve non-numeric objects");
}

template<class T> template<typename U>  //example: T=int, U=float
typename std::enable_if_t<std::is_arithmetic<T>::value && std::is_arithmetic<U>::value, U>
Test<T>::halve() {  //error C2244 - unable to match function definition to an existing declaration
    return ((U)val_ / (U)2);
}


int main() {
    Test<int> t1(5);
    float f = t1.halve<float>();    //expect 2.5
    int i = t1.halve<int>();        //expect 2
    Test<std::string> t2((std::string)"blah");
    int t2h = t2.halve<int>();  //this will throw (by design)
    return 0;
}

【问题讨论】:

  • 您不能在 C++ 中部分特化函数。差不多就是这样。你必须把它变成类的部分专业化。这基本上意味着将halve() 转变成一个瘦包装器,它调用辅助类中的真实方法,即来自template&lt;typename T, typename U&gt; class halve_helperhalve_helper&lt;T,U&gt;::real_halve(val_);,然后部分特化halve_helper。祝你好运。
  • 谢谢山姆。这有助于我更好地理解我的问题几乎重复的最佳答案:link。我将尝试那里描述的两种方法(转化为你的方法,以及 kjpus 的建议),看看哪一种看起来更易读和可维护。

标签: c++ c++14


【解决方案1】:

Sam 的评论是正确的——你不能部分专门化成员函数。解决方法是在类主体中使用enable_if_t。这样的事情应该可以工作:

template <class T> class Test {
  public:
    Test(T val) { val_ = val; }
    template <typename U>
      std::enable_if_t<std::is_arithmetic<T>::value && std::is_arithmetic<U>::value, U>
      halve () {  
         return ((U)val_ / (U)2);
      }
    template <typename U>
      std::enable_if_t<!(std::is_arithmetic<T>::value && std::is_arithmetic<U>::value), U>
      halve () {  
          throw std::invalid_argument("Cannot halve non-numeric objects");
      }
  private:
    T val_;
};

【讨论】:

  • 具有讽刺意味的是,这就是我开始的地方——涵盖 is_arithmetic() 方面和 !is_arithmetic() 方面,认为这将是“完全”专业化。现在让我非常困惑的是为什么当你将方法定义放在类声明中时它会起作用,但是当你在那里向前声明它们并在声明区域之外定义它们时它会失败(即使用template&lt;class T&gt; template&lt;typename U&gt; std::enable_if_t&lt;!(std::is_arithmetic&lt;T&gt;::value &amp;&amp; std::is_arithmetic&lt;U&gt;::value), U&gt; Test&lt;T&gt;::halve() {...} .
  • 也许我开始明白了。这是成员函数的条件声明(实际上是两次),而不是无条件声明的成员函数的特化。当我在类声明中前向声明方法并稍后定义它们时,它是成员函数的特化。对吗?
  • 我对行话不是很熟悉,但你的 OP 是模板专业化,而我的是 SFINAE(我认为)。您可以搜索 SO 并找到更多关于差异的帖子,例如this post.
【解决方案2】:

有一种更简单的方法可以做到这一点,只需使用模板声明一个主函数,然后将其专门用于您需要的任何类型。我添加了int和float,它返回了正确的值。

传入任何其他内容并抛出,因为第一个 halve 函数将被调用。

template <class T>
class Test
{
public:

    Test(T val)
    {
        val_ = val;
    }

    // Default function
    template<class MainType>
    MainType halve()
    {
        throw std::invalid_argument("Cannot halve non-numeric objects");
    }

    // Int specialization
    template<>
    int halve<int>()
    {
        return (static_cast<int>(this->val_) / static_cast<int>(2));
    }

    // Float specialization
    template<>
    float halve<float>()
    {
        return (static_cast<float>(this->val_) / static_cast<float>(2));
    }

private:

    T val_;
};

int main()
{
    Test<int> t1(5);
    float f = t1.halve<float>();    // Returns 2.5
    int i = t1.halve<int>();        // Returns 2
    void* v = t1.halve<void*>();    // Throws. Type void* is not specialized in the class
    printf("%.5f %d\n", f, i);
    getchar();
    return 0;
}

【讨论】:

  • 谢谢,但必须声明所有单独的重载正是我想要避免的。我的实际用例比这个简化的示例有更多的组合,并且重复该方法无数次只是我希望避免的高维护容易出错的混乱。从维护的角度来看,构建它的最简单方法通常不是最好的方法。
猜你喜欢
  • 2013-12-27
  • 1970-01-01
  • 2020-07-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多