根据模板值选择方法代码

时间:2014-11-26 17:30:58

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

我有一个模板c ++对象,如下所示

template <typename T, Dimension D>
class Field : public std::vector<T>
{
    // ... lot of stuff ...
    T differentiate(const gridPoint<D>&, int) const;
};

根据Dimension D

,这种区分方法的计算方式不同
enum Dimension : std::size_t { _2D = 2, _3D = 3 };

我可以在方法体内放置一个开关bt我想使用模板以帮助清晰

我尝试使用std::enable_if这样:

template <typename T, Dimension D>
typename std::enable_if<D==_2D, T>::type
Field<T,D>::differentiate(const gridPoint<D>& pt, int extent) const
{
    // ... lot of computation
}

template <typename T, Dimension D>
typename std::enable_if<D==_3D, T>::type
Field<T,D>::differentiate(const gridPoint<D>& pt, int extent) const
{
    // ... even more computation
}

但编译器告诉我,我的实现与任何原型都不匹配

我做错了什么?我只是无法弄清楚我是如何声明方法的代码

3 个答案:

答案 0 :(得分:2)

从长远来看,通过为2D和3D案例编写Field的不同部分特化,你可以节省很多麻烦和难以理解的代码:

enum Dimension : std::size_t { _2D = 2, _3D = 3 };

template <Dimension D>
using gridPoint = std::array<int, D>;

template <typename T>
struct Field_base : std::vector<T> {
    // Stuff common to both specializations goes here.
    using std::vector<T>::vector;
};

template <typename, Dimension>
struct Field;

template <typename T>
struct Field<T, _2D> : Field_base<T>
{
    using grid_point = gridPoint<_2D>;
    using Field_base<T>::Field_base;

    T differentiate(const grid_point&, int) const
    {
        std::cout << "2D differentiate called\n";
        return {};
    }
};

template <typename T>
struct Field<T, _3D> : Field_base<T>
{
    using grid_point = gridPoint<_3D>;
    using Field_base<T>::Field_base;

    T differentiate(const grid_point&, int) const
    {
        std::cout << "3D differentiate called\n";
        return {};
    }
};

答案 1 :(得分:1)

为了使SFINAE正常工作,我认为函数需要进行模板化,这样就可以选择在重载决策期间编译哪个函数,而不是在类实例化过程中编译哪个函数。

我修改了这个如下,它&#34;工作&#34;为此:

#include <iostream>
#include <vector>

enum Dimension : std::size_t { _2D = 2, _3D = 3 };

template <Dimension D>
struct gridPoint
{
    int d[D];
};

template <typename T, Dimension D>
struct Field : public std::vector<T>
{
    template <Dimension D2>
    typename std::enable_if<D== D2 && D==_2D, T>::type
    differentiate(const gridPoint<D2>& pt, int extent) const
    {
        std::cout << "2D differentiate called" << std::endl;
        return T(0.0);
    }

    template <Dimension D2>
    typename std::enable_if<D==D2 && D==_3D, T>::type
    differentiate(const gridPoint<D2>& pt, int extent) const
    {
        std::cout << "3D differentiate called" << std::endl;
        return T(0.0);
    }
};

int main() {
    Field<double, _2D> foo;
    gridPoint<_2D> point { 3, 4 };
    foo.differentiate(point, 3);

    gridPoint<_3D> p3 { 3, 4, 5 };
    Field<double, _3D> bar;
    bar.differentiate(p3, 8);

    return 0;
}

我没有对模板foo进行整理,以便使用定义外线进行编译。

答案 2 :(得分:1)

您尝试的外线SFINAE定义引发的编译器错误 对于T Field<T,Dimension>::differentiate(const gridPoint<D>&, int) const,将是:

error: prototype for ‘typename std::enable_if<(D == _2D), T>::type Field<T, D>::differentiate(const gridPoint<D>&, int) const’ does not match any in class ‘Field<T, D>’
error: candidate is: T Field<T, D>::differentiate(const gridPoint<D>&, int) const
error: prototype for ‘typename std::enable_if<(D == _3D), T>::type Field<T, D>::differentiate(const gridPoint<D>&, int) const’ does not match any in class ‘Field<T, D>’
error: candidate is: T Field<T, D>::differentiate(const gridPoint<D>&, int) const

或者说是这样的话。

编译器坚持任何声称的成员函数的外联定义 Field<T,Dimension>的原型与声明的原型相同 一些成员函数,它的诊断说明这个要求是 不满意任何一种所谓的外线定义。

如果编译器只是继续执行SFINAE,那就不好抗议了, 然后它会发现一个幸存的外线定义与成员函数声明匹配。它做不到 SFINAE直到尝试Field<D,Dimension>的某些实例化,并确保这一点 任何与模板/类成员声明配对的外部模板/类成员定义都在它的前面 待办事项列表比实例化模板。实例化可能永远不会发生,但孤儿 成员定义总是错误的。

因此,这些SFINAE装饰原型的必须作为成员出现 功能声明。

但是,如果编译器要容忍这两个SFINAE装饰的成员 函数声明,它们必须是模板成员函数(不仅仅是 类模板的成员函数)及其各自的std::enable_if条件 必须依赖于成员函数的模板参数。这就是SFINAE 规则。

总结一下,您需要编写什么来完成您的外线SFINAE定义 由以下程序说明:

#include <iostream>
#include <vector>

enum Dimension : std::size_t { _2D = 2, _3D = 3 };

template<Dimension D>
struct gridPoint {}; // ...whatever

template <typename T, Dimension D>
class Field : public std::vector<T>
{
public:
    template <Dimension Dim = D>
    typename std::enable_if<Dim ==_2D, T>::type
    differentiate(const gridPoint<Dim>&, int) const;

    template <Dimension Dim = D>
    typename std::enable_if<Dim ==_3D, T>::type
    differentiate(const gridPoint<Dim>&, int) const;
};

template <typename T, Dimension D> template<Dimension Dim>
typename std::enable_if<Dim ==_2D, T>::type
Field<T,D>::differentiate(const gridPoint<Dim>& pt, int extent) const
{
    std::cout << "_2D differentiate" << std::endl;
    return T(); // ...whatever
}

template <typename T, Dimension D> template<Dimension Dim>
typename std::enable_if<Dim ==_3D, T>::type
Field<T,D>::differentiate(const gridPoint<Dim>& pt, int extent) const
{
    std::cout << "_3D differentiate" << std::endl;
    return T(); // ...whatever
}


int main()
{
    Field<int,_2D> f_2d;
    gridPoint<_2D> gp_2d;
    f_2d.differentiate(gp_2d,2);
    Field<float,_3D> f_3d;
    gridPoint<_3D> gp_3d;
    f_3d.differentiate(gp_3d,3);
    f_3d.differentiate(gp_2d,2);
    return 0;
}

在这不太令人愉快的情况下,您可能想要查看Dimension 是否需要作为模板参数的问题 Field,或者它是否只是Field的成员函数的模板参数。就像我一样 不知道模板的完整实现,我不能说。或者,你可能会重新考虑你不喜欢的 模板基类+ @casey建议的部分专业化方法。

大概你想要differentiate的替代定义,因为它们很大并且你不想要它们 在类模板的主体中蔓延。在像这样棘手的情况下,一种模糊但相当安全的方式将模板/类成员定义挤出到线外 首先使用内联定义对模板/类进行编码并获得成功构建;然后将内联定义复制粘贴到它们的 在线外的地方,添加所需的模板/类资格并删除默认说明符;然后将原始的内联定义截断为声明; 然后再次成功构建。

该示例程序的输出是:

_2D differentiate
_3D differentiate
_2D differentiate

最后一行由执行f_3d.differentiate(gp_2d,2)发出,这引起了对differentiate的选定实现的关注 由传递给它的Dimension参数的gridPoint<Dimension>&确定,而不是由Dimension的{​​{1}}确定 调用它的Field<T,Dimension>。因此,我们可以致电Field<T,_3D>::differentiate<_2D>。既然你说:

  

根据维度D

,不同地计算该区分方法
从表面来看,这似乎是你想要的行为,gridPoint<Dimension> 参数根据的值区分differentiate的实现 Dimension。但是这一观点再次回答了这个问题:是否真的有充分的理由 DimensionField的模板参数,而不只是differentiate的模板参数?

如果确实有充分理由,那么您希望无法拨打Field<T,_3D>::differentiate<_2D>Field<T,_2D>::differentiate<_3D>。您可以通过替换所有出现的<Dim>来实现 <D>在该计划中,尽管SFINAE的实施看起来更加艰难。

相关问题