2

我目前正在开发一个使用表达式模板的数值库。不幸的是,我遇到了运算符重载的问题。考虑以下精简示例。

#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),这会导致很多不同的组合。

有没有办法在不为每个运算符拼出所有组合的情况下完成这项工作?

4

2 回答 2

2

这是因为end()返回的类型是类模板特化,它有一个 type 参数test::test *。因此,当operator-在表达式中应用 时end() - 1,依赖于参数的查找也在. 的命名空间中查找test::test。它找到你的operator-并将它传递给迭代器和一个int.

您可以通过不接受任何和所有类型作为参数来修复它。例如,尝试接受(testExpr<A1, B1>, testExpr<A2, B2>)。展示你所有的组合,可能有一种方法可以用另一种方式来减少它们?

在我看来,以这种方式行事的实现应该是不合格的(虽然我认为这真的很恶心)。因为iterator - 1do 被指定为前一个元素产生另一个迭代器,并且不能做那样疯狂的事情,我认为。一种方法是将运算符声明为直接接受迭代器类型和整数参数(属于迭代器的)的非模板difference_type。这样他们的版本应该总是首选。

于 2010-10-07T20:38:48.633 回答
0

您的代码在 VC++ 版本 10(来自 Visual Studio 2010 C++ Express)中编译正常,即使修改如下:

int main()
{ 
    vector<test::test> vec;
    test::test instance = stuff(vec);

    return 0;
}

这可能是编译器的限制。表达式模板在某种程度上是对编译器模板支持的压力测试。

于 2010-10-07T14:18:33.730 回答