views:

90

answers:

2

I'm currently working on a numerical library that uses expression templates. Unfortunately I encountered a problem with my operator overloads. Consider the following stripped down example.

#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()
{ }

Which gives the following error message when compiling with gcc 4.4.3 or 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.

For some reason the compilers do a lookup into the test namespace and find my general operator. I used this form together with some traits magic to reduce the number of version i had to make for the operator. It should accept 4 different datatypes (including double and int) which would lead to a lot of different combinations.

Is there any way to make this work without spelling out all combinations for every operator?

A: 

Your code compiles OK in VC++ version 10 (from Visual Studio 2010 C++ Express), even when modified like this:

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

    return 0;
}

This could be a limitation of the compilers. Expression templates are somewhat of a stress test for a compiler's template support.

Steve Townsend
+2  A: 

This is because end() returns a type that is a class template specialization which has one argument of type test::test *. Thus when operator- is applied in the expression end() - 1, argument dependent lookup looks also in the namespace of test::test. It finds your operator- and passes it the iterator and an int.

You could fix it by not accepting any and all types as arguments. For example, try accepting (testExpr<A1, B1>, testExpr<A2, B2>) instead. Show all your combinations, possibly there is a way to cut them down using another way to formulate them?

In my opinion, an implementation that acts that way should be non-conforming (I think this is really disgusting though). Because doing iterator - 1 is specified to yield another iterator to the previous element and must not do something crazy like that, I think. One way for it is to declare operator as a non-template accepting the iterator type and the integer argument (which is of the iterator's difference_type) directly. This way their version should always be preferred.

Johannes Schaub - litb
While I'm still not sure if this is a bug or an inaccuracy in the standard, I decided to ship around by generating the necessary combinations with a script.
ebo