我需要实现一种通用算法,该算法可以对矩阵进行运算,而不管其表示形式如何。它可以是 C 风格的二维数组、向量的向量或任意用户类(不一定提供下标运算符)。 一个明显的解决方案可能如下所示:
template<typename M>
void func(M& mat, int cols, int rows)
{
for(int j = 0; j < rows; ++j)
for(int i = 0; i < cols; ++i)
doSomething(elem(mat, i, j));
}
...用户必须提供对其矩阵类型进行操作的“elem”重载。这种方法的问题在于,为了编译代码,需要在“func”之前声明此重载,而不仅仅是在模板实例化之前。有没有解决这个问题的方法,既不涉及丑陋的函数签名,也不强制用户编写包装类和其他样板代码?
最佳答案
我已经尝试过了,并在我发表第一条评论后找到了可能的解决方案。所以只需将声明包含到通用函数中,它就可以工作!请参阅下面的示例代码。
最后一点似乎是templetized 代码没有得到elem()
的声明。在非模板化 代码中完成。 罢工>
它在模板定义之后,但在它的第一次实例化之前,所以,据我所知/理解,它应该足够了......但我的编译器(gcc 4.8.2)也提示。我肯定已经多次通过模板化方法和类使用此功能。
这对我来说真的很奇怪,可能是一个错误(@Potatoswatter:你能提供一个错误的引用 - 看看这是否匹配吗?)。 罢工>
编辑:终于明白了。还在学习C++11 Stroustrup's !它按照标准中的预期工作。我在这里给出一些建议 - 摘录。
第一个重要思想在 23.3.2 中提出(模板,错误检测):语法错误在第一次使用之前在定义中被检查。当然他们是,但尽管它只是后来定义的。但是:“模板定义中使用的名称必须在范围内或以某种相当明显的方式依赖于模板参数”。这一点现在已经很清楚了,但最重要的是这个想法背后的基本原理。
在 26.3([模板的实例化!],名称绑定(bind))中有非常详细的解释:“定义模板函数以最小化对非本地信息的依赖。原因是模板将用于基于未知类型和未知上下文生成函数和类。每个微妙的上下文依赖性都可能作为某人的问题浮出水面...”。
阅读之后 - 我还在问自己,为什么我没有考虑到如此重要的区别,关于通用类中存在的受控环境!!
继续解释(第 745-758 页!),尤其是在 26.3.2(定义点绑定(bind))和 26.3.3(点-实例化绑定(bind)):
“当编译器看到一个模板定义时,它会确定哪些名称是依赖的(26.3.1)。如果一个名称是依赖的,则查找它的声明将推迟到实例化时间(26.3.3)。”
“不依赖于模板参数的名称被视为不在模板中的名称;它们必须在定义点的范围 (6.3.4) 中”。
那是被扔石头了。 elem()
必须在模板定义中使用之前声明 - 它被视为不在模板中的名称。
我同意@Potatoswatter 和其他人的观点。这可能是不太优雅的解决方案,因为它仅限于使用外部函数,没有仿函数,没有 lambda。
另一方面,它解决了 OP 的问题(最初认为这是一种解决方法……不,这就是它的工作方式!)。
#include <iostream>
using namespace std;
template<typename M, typename R>
void func(M mat, int cols, int rows)
{
// with this declaration it works.
R &elem(M, int, int);
for(int i = 0; i < cols; ++i) {
for(int j = 0; j < rows; ++j) {
elem(mat, i, j) += 1; // +=1 is just your "doSomething()"
}
}
}
template<typename M, typename R>
void show(M mat, int cols, int rows)
{
R &elem(M, int, int);
for(int i = 0; i < cols; ++i) {
for(int j = 0; j < rows; ++j) {
if (j>0) cout << ", ";
cout << elem(mat, i, j);
}
cout << endl;
}
}
float &elem(float *m, int i, int j) {
return m[i*3+j];
}
float &elem(float m[3][3], int i, int j) {
return m[i][j];
}
int main(int argc, char **argv) {
float mat1d[9] = {1,2,3,4,5,6,7,8,9};
float mat2d[3][3] = {1,2,3,4,5,6,7,8,9};
func<float*, float>(mat1d, 3, 3);
show<float*, float>(mat1d, 3, 3);
func<float(*)[3], float>(mat2d, 3, 3);
show<float(*)[3], float>(mat2d, 3, 3);
}
尝试像您的问题一样使用引用,在理解将它们与静态声明的大小混合之前,我有点疯狂,让事情变得更加卡住。我把它放在这里是因为我已经浪费了很多时间来解决这个问题:
#include <iostream>
using namespace std;
template<typename M, typename R>
void func(M &mat, int cols, int rows)
{
R &elem(M&, int, int);
for(int i = 0; i < cols; ++i) {
for(int j = 0; j < rows; ++j) {
elem(mat, i, j) += 1; // +=1 is just your "something"
}
}
}
template<typename M, typename R>
void show(M &mat, int cols, int rows)
{
R &elem(M&, int, int);
for(int i = 0; i < cols; ++i) {
for(int j = 0; j < rows; ++j) {
if (j>0) cout << ", ";
cout << elem(mat, i, j);
}
cout << endl;
}
}
float &elem(float (&m)[9], int i, int j) {
return m[i*3+j];
}
float &elem(float (&m)[3][3], int i, int j) {
return m[i][j];
}
int main(int argc, char **argv) {
float mat1d[9] = {1,2,3,4,5,6,7,8,9};
float mat2d[3][3] = {1,2,3,4,5,6,7,8,9};
func<float[9], float>(mat1d, 3, 3);
show<float[9], float>(mat1d, 3, 3);
func<float[3][3], float>(mat2d, 3, 3);
show<float[3][3], float>(mat2d, 3, 3);
}
注意:以这种方式elem()
是一个函数,包含在链接时。我认为这不是您想要的,但是您可以绕过它,将所有内容都作为仿函数。
关于templates - 可扩展的临时多态性? (C++11),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22101489/