【问题标题】:How to work around partial specialization of function template?如何解决功能模板的部分专业化?
【发布时间】:2014-03-07 15:18:20
【问题描述】:

例如,我有一个班级:

class A
{
    enum {N = 5};
    double mVariable;

    template<class T, int i>
    void f(T& t)
    {
        g(mVariable); // call some function using mVariable.
        f<T, i+1>(t); // go to next loop
    }

    template<class T>
    void f<T, N>(T& t)
    {} // stop loop when hit N.
};

函数模板中不允许部分特化。我该如何解决这个问题?

我稍微改变了 Arne Mertz 的例子,比如:

template<int n>
struct A
{
    enum {N = n};
    ...
};

并使用 A like:

A<5> a;

我无法在 Visual Studio 2012 上编译。是编译器错误还是其他原因?这很奇怪。

编辑:已选中。这是一个 Visual Studio 错误。 :(

我认为 Nim 提供了最简单的实现方式。

【问题讨论】:

  • 您不使用for 循环的任何原因?
  • I tried the templated version on gcc 它编译得很好。您甚至可以完全保留枚举并将 N 本身作为模板参数。
  • 使用形参t有什么意义?
  • @Constructor 是模板参数T类型。比如可以把A的内容写到不同类型的变量t中。
  • @user1899020 用在什么地方?

标签: c++ templates template-specialization


【解决方案1】:

最直接的解决方案是使用模板类而不是函数:

class A
{
    enum {N = 5};
    double mVariable;

    template <class T, int i>
    struct fImpl {
      static_assert(i<N, "i must be equal to or less than N!");
      static void call(T& t, A& a) {
        g(a.mVariable);
        fImpl<T, i+1>::call(t, a);
      }
    };

    template<class T>
    struct fImpl<T,N> {
      static void call(T&, A&)  {} // stop loop when hit N.
    };

 public:

    template<class T, int i>
    void f(T& t)
    {
        fImpl<T, i>::call(t,*this);
    }

};

Example link

【讨论】:

  • ffImpl::call的第一次调用不应该被i索引吗?
  • +1 但只是一个挑剔:如果A 本身就是一个类模板,那么在专门化嵌套模板时可能会遇到困难(取决于部分专门化还是完全专门化)。所以在这种情况下,最好将这些东西移动到 detail 命名空间中。
  • @TemplateRex 是的,但在这种情况下,fImpl 必须成为 A 的所有实例化的朋友,或者成为 A 所需内部结构的句柄(在这种情况下,@ 987654331@) 必须传递。也应该考虑将fImplmVariable 移入非模板基类。
  • 这个方法很简单。如上所述,我稍微修改了您的示例。我得到编译错误。请你看一下好吗?谢谢。我用其他编译器检查了它。这是一个 Visual Studio 错误。
【解决方案2】:

你可以定义一个辅助类:

template <int i, int M>
struct inc_up_to
{
  static const int value = i + 1;
};

template <int i>
struct inc_up_to<i, i>
{
  static const int value = i;
};


template<class T, int i>
void f(T& t)
{
    if (i < N) {
        g(mVariable); // call some function using mVariable.
        f<T, inc_up_to<i, N>::value>(t);
    }
}

它通过使f&lt;T, N&gt; 引用f&lt;T, N&gt; 来停止编译时递归,但是运行时条件避免了该调用,从而中断了循环。

帮助器的简化和更强大的版本(感谢@ArneMertz)也是可能的:

template <int i, int M>
struct inc_up_to
{
  static const int value = (i >= M ? M : i + 1); // this caps at M
  // or this:
  static const int value = (i >= M ? i : i + 1); // this leaves i >= M unaffected
};

这甚至不需要部分专业化。

【讨论】:

  • 您可以简化事情通过仅定义value = (M&gt;=N) ? i : i+1 的常见情况并完全省略专业化来捕获我们所有实现中的i&gt;N 问题。跨度>
【解决方案3】:

借助 c++11 支持,您可以执行以下操作:

#include <iostream>
#include <type_traits>
using namespace std;

struct A
{
    enum {N = 5};
    double mVariable;

    void g(int i, double v)
    { std::cout << i << "  " << v << std::endl; }

    template<int i, class T>
    typename enable_if<i >= N>::type f(T& t)
    {} // stop loop when hit N.

    template<int i, class T>
    typename enable_if<i < N>::type f(T& t)
    {
        g(i, mVariable); // call some function using mVariable.
        f<i+1, T>(t); // go to next loop
    }

};

int main(void)
{
    A a;
    int v = 0;
    a.f<0>(v);
}

我喜欢的主要原因是您不需要前面的答案所要求的任何麻烦...

【讨论】:

  • 只是想补充一点,从技术上讲,您不需要 C++ 11。 enable_if 是一个可以使用 SFINAE 在任何 C++ 版本中实现的类。
  • 当然在现实生活中struct A的定义可能在头文件中,所以你当然不会在结构体之前输入'using namespace std;',而只会拼出std::enable_if
  • 警告:在我离开期间,似乎有几个人修改了我的原始代码。我使用别名是有一个原因,它让整个元编程方面的事情变得更容易......
  • @Nim 它使打字更容易,但对于还不了解这些内容的人来说,阅读起来并不容易。
【解决方案4】:

您可以通过函数重载来模拟函数模板的部分特化:

#include <type_traits>

class A
{
    enum {N = 5};
    double mVariable;

    // ...

    void g(double)
    {
        // ...
    }

public:

    template<class T, int i = 0>
    void f(T& t, std::integral_constant<int, i> = std::integral_constant<int, i>())
    {
        g(mVariable);
        f(t, std::integral_constant<int, i + 1>());
    }

    template<class T>
    void f(T& t, std::integral_constant<int, N>)
    {
    }

};

使用示例:

A a;
int t = 0;

a.f(t);
a.f(t, std::integral_constant<int, 2>()); // if you want to start loop from 2, not from 0

然而,这是一个 C++11 解决方案(不是因为 std::integral_constant 类,而是因为函数模板的默认模板参数)。可以使用一些额外的 C++11 特性来缩短它:

template<int i>
using integer = std::integral_constant<int, i>;

template<class T, int i = 0>
void f(T& t, integer<i> = {})
{
    g(mVariable);
    f(t, integer<i + 1>());
}

template<class T>
void f(T& t, integer<N>)
{
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-04-12
    • 1970-01-01
    • 2020-06-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-18
    相关资源
    最近更新 更多