我目前正在研究一个使用表达式模板的数值库。不幸的是我遇到了运算符重载的问题。请考虑以下精简示例。
#include <vector>
namespace test {
class test {};
template<class A, class B>
class testExpr {};
template<class A, class B>
testExpr<A, B>
operator-(A a, B b)
{
return testExpr<A, B>();
}
}
test::test
stuff(std::vector<test::test> &v)
{ return v.back(); }
int main()
{ }
使用gcc 4.4.3或clang 2.8编译时出现以下错误消息:
In file included from eir_test.cc:2:
In file included from /usr/include/c++/4.4/vector:64:
/usr/include/c++/4.4/bits/stl_vector.h:696:16: error: indirection requires pointer operand
('testExpr<__gnu_cxx::__normal_iterator<test::test *, std::vector<test::test, std::allocator<test::test> > >, int>' invalid)
{ return *(end() - 1); }
^~~~~~~~~~~~
eir_test.cc:21:12: note: in instantiation of member function 'std::vector<test::test, std::allocator<test::test> >::back' requested here
return v.back();
^
1 error generated.
由于某种原因,编译器会查找测试命名空间并找到我的常规运算符。我使用这种形式以及一些特性魔法来减少我必须为操作员制作的版本数量。它应该接受4种不同的数据类型(包括double和int),这将导致许多不同的组合。
有没有办法让这项工作没有为每个操作员拼出所有组合?
答案 0 :(得分:2)
这是因为end()
返回的类型是一个类模板特化,它有一个类型为test::test *
的参数。因此,当在operator-
表达式中应用end() - 1
时,依赖于参数的查找在test::test
的名称空间中看起来也是 。它找到你的operator-
并将迭代器和int
传递给它。
您可以通过不接受任何和所有类型作为参数来修复它。例如,请尝试接受(testExpr<A1, B1>, testExpr<A2, B2>)
。显示你所有的组合,可能有一种方法可以用另一种方式来制定它们吗?
在我看来,以这种方式行事的实施应该是不符合的(我认为这真的很恶心)。因为我认为执行iterator - 1
是为了产生前一个元素的另一个迭代器而且不能做那样疯狂的事情。一种方法是将operator声明为非模板,直接接受迭代器类型和整数参数(迭代器的difference_type
)。这样他们的版本应该始终是首选。
答案 1 :(得分:0)
您的代码在VC ++版本10(来自Visual Studio 2010 C ++ Express)中编译正常,即使修改如下:
int main()
{
vector<test::test> vec;
test::test instance = stuff(vec);
return 0;
}
这可能是编译器的限制。表达式模板在某种程度上是对编译器模板支持的压力测试。