C++模板 - 成员函数的部分特化

3

我正在尝试根据模板参数将某些几何函数特化为2D或3D。最好如果我包含一些(非常不完整的)用于玩具版本问题的代码:

template <typename T, int d>
class Point
{
public:
    int x;
    int y;
    int z;

    T add ()
    {
        return T(0);
    }

    template <>
    T add <T, 2> ()
    {
        return x + y;
    }

    template <>
    T add <T, 3> ()
    {
        return x + y + z;
    }
};

这段代码无法编译。我尝试了很多不同的模板参数格式和类定义,但无法找到一种方法来对“d”进行函数专门化,同时保持“T”的通用性。

在我的实际解决方案中,我正在尝试计算梯度、曲率、插值等针对2D或3D情况的特定内容。有些东西,比如梯度计算,可以简单地使用“d”参数来限制for循环迭代。而其他一些,比如插值,需要为2D和3D分别提供一个函数。

任何提示都非常感谢!


1
模板参数 T 用于什么?它是否应该是您的坐标类型?如果是这样,为什么 x、y、z 被声明为 int 而不是类型 T? - Vaughn Cato
只是为了澄清一下:不存在所谓的“模板特化”,你想要的是“模板重载”。 - user1849534
@Vaughn:这个简单的玩具问题源于我需要一个二维或三维网格,可以在每个网格点上包含任意类型的值(尽管通常只是int、float或double)。因此,x、y(,z)作为坐标,T作为该坐标处值的类型。 - Dave
2个回答

7
我建议采用以下解决方案:
template <typename T, int d> 
class Point : public Point<T, d-1>
{
   typedef Point<T, d-1> base;
   T m_value;
public:
    T add()
    {
        return m_value + base::add();
    }   

    //another method which returns you the point value
    template<int N>
    T get()
    {
       return N==d ? m_value : base::get<N>();
    }
};

template <typename T>
class Point<T,0>
{
protected:
    T add()
    {
        return T(); //default value which is zero for all builtin types
    }
    template<int N>
    T get() { return T(); }

};

使用这个解决方案,您可以拥有任意多的点,但必须大于零。
Point<int,1> p1;  //contains 1 point
Point<int,2> p2;  //contains 2 points
Point<int,3> p3;  //contains 3 points
Point<int,4> p4;  //contains 4 points
Point<int,5> p5;  //contains 5 points

auto x1 = p5.get<1>(); //get first point
auto x3 = p5.get<3>(); //get third point
auto x4 = p5.get<4>(); //get fourth point

或者出于方便,可以使用以下typedef:

typedef Point<int,2> Point2D;
typedef Point<int,3> Point3D;

//then use them
Point2D p2d;
Point3D p3d;

这只是一个基本的想法,可以进一步增强,支持许多有用的功能。我只是写了get<>来演示一个似乎很有用的功能。


1
更好的方法。唯一的缺点是成员失去了有意义的名称。 - K-ballo
1
@K-ballo: 你可以随时使用 enable_if<> 在 d==1, d==2, ... 条件下有条件地添加 getX(), getY() 等等。 - Ben Jackson
1
@Ben Jackson:你不能使用enable_if来限制getX(),因为那里没有模板参数。如果我们要这样做,我更喜欢get<0> - K-ballo
@Nawaz:我认为get<1>更好地表示通用的getY(),而不是getX() ;) - K-ballo
1
哈哈,我喜欢这个,非常巧妙!它解决了我上面玩具问题中的非常特殊的情况。将我的问题简化为一个玩具问题的想法只是为了突出一个事实,即我不知道如何在单个模板参数上进行成员函数特化,同时保持其他参数通用。Vaughn的解决方案更接近我实际想要的,只是它需要对整个类进行特化,而不仅仅是一些函数。 - Dave
显示剩余2条评论

5

以下是您的示例可能的工作方式。首先,您需要声明主模板:

template <typename T, int d> class Point;

不需要定义它,因为您没有通用实现。

接下来,您将为不同数量的维度创建部分特化,但您的部分特化仍将将类型T作为模板参数:

template <typename T>
class Point<T,2>
{
public:
    T x;
    T y;

    T add()
    {
        return x + y;
    }
};

template <typename T>
class Point<T,3>
{
public:
    T x;
    T y;
    T z;

    T add()
    {
        return x + y + z;
    }
};

好的,唯一的问题是如果我想要在专用(或重写)函数旁边拥有通用函数,我该怎么做?我不想专门化整个类,当只有几个函数实际上是不同的。 - Dave
@Dave:一种方法是拥有一个派生类(Derived Class),其中包含通用函数但没有特化,然后使其从一个具有特化的基类(Base Class)继承。 - Vaughn Cato
我怀疑我可能不得不使用子类化而不是模板特化。好的,那么,我想我已经得到了答案。 - Dave

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接