我有一个模板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
}
但编译器告诉我,我的实现与任何原型都不匹配
我做错了什么?我只是无法弄清楚我是如何声明方法的代码
答案 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
。但是这一观点再次回答了这个问题:是否真的有充分的理由
Dimension
是Field
的模板参数,而不只是differentiate
的模板参数?
如果确实有充分理由,那么您希望无法拨打Field<T,_3D>::differentiate<_2D>
或Field<T,_2D>::differentiate<_3D>
。您可以通过替换所有出现的<Dim>
来实现
<D>
在该计划中,尽管SFINAE的实施看起来更加艰难。