10

I want to use a simple struct with member variables named start and end in a function template:

#include <iostream>
using namespace std;

struct st {
    int start;
    int end;
};

template<typename T>
void compare(const T& v1, const T& v2){
    if(v1.end < v2.end)
        cout << "v1 < v2" << endl;
}

int main() {
    st a = {1, 2};
    st b = {2, 3};
    compare(a, b);
    return 0;
}

But this program fails to compile on mingw g++ 4.8.2 with:

main.cpp: In function 'void compare(const T&, const T&)':
main.cpp:11:11: error: parse error in template argument list
     if(v1.end < v2.end)
           ^
main.cpp: In instantiation of 'void compare(const T&, const T&) [with T = st]':
main.cpp:18:17:   required from here
main.cpp:11:5: error: 'end' is not a member template function
     if(v1.end < v2.end)
     ^

Why not? What's wrong with my code?

Barry
  • 286,269
  • 29
  • 621
  • 977
xiao
  • 113
  • 5
  • what's the exact error message? BTW, Test type is not defined anywhere. – billz Jul 29 '15 at 04:13
  • 1.The line "Test test" should delete and I forget. I want to a mini code to read. 2. the error message is : test.cpp: In member function 'void compare(const T&, const T&)': test.cpp:14:15: error: parse error in template argument list if(v1.end < v2.end) – xiao Jul 29 '15 at 04:22

4 Answers4

10

This is clearly a gcc bug (specifically 10200 although there are several dupes with lots of different examples). [temp.names] states:

When the name of a member template specialization appears after . or -> in a postfix-expression or after a nested-name-specifier in a qualified-id, and the object expression of the postfix-expression is type-dependent or the nested-name-specifier in the qualified-id refers to a dependent type, but the name is not a member of the current instantiation (14.6.2.1), the member template name must be prefixed by the keyword template. Otherwise the name is assumed to name a non-template. [ Example:

struct X {
    template<std::size_t> X* alloc();
    template<std::size_t> static X* adjust();
};

template<class T> void f(T* p) {
    T* p1 = p->alloc<200>();          // ill-formed: < means less than
    T* p2 = p->template alloc<200>(); // OK: < starts template argument list
    T::adjust<100>();                 // ill-formed: < means less than
    T::template adjust<100>();        // OK: < starts template argument list
}

—end example ]

v1 and v2 are type-dependent, so the name should be assumed to name a non-template due to the omitted template keyword and the < should be treated as less than, exactly as in the example above.

Not to mention that [basic.lookup.classref] states that:

The identifier is first looked up in the class of the object expression. If the identifier is not found, it is then looked up in the context of the entire postfix-expression and shall name a class template.

And end should clearly be found in the class of the object expression - it's a simple member variable, after all. The fact that it's a failure for end only because of the collision with std::end() further supports the idea of bug since that scope should never have been considered to begin with.

Amusingly, the simplest solution is simply: don't use using namespace std;!

Community
  • 1
  • 1
Barry
  • 286,269
  • 29
  • 621
  • 977
8

Actually it's the < which confuse the compiler, since it does not know whether it's the start of a template expression, or the comparator.

Since @R Sahu asked for an official sources, here is the explanation :

The paragraph that matters here is [basic.lookup.classref]p1:

"In a class member access expression (5.2.5), if the . or -> token is immediately followed by an identifier followed by a <, the identifier must be looked up to determine whether the < is the beginning of a template argument list (14.2) or a less-than operator. The identifier is first looked up in the class of the object expression. If the identifier is not found, it is then looked up in the context of the entire postfix-expression and shall name a class template."

Since v is dependent, presumably the identifier is not found so we consider what happens if we look in the context of the entire postfix-expression. Since we find a function template, we should not conclude that we have the start of a template-id.

Source : C++ confusing attribute name for member template

And here is the corresponding gcc bug : https://gcc.gnu.org/bugzilla/show_bug.cgi?id=10200

* Paolo Carlini 2015-06-23 15:03:03 UTC *

Bug 65878 has been marked as a duplicate of this bug.

* Colin MacKenzie 2015-06-23 22:19:29 UTC *

Had this one today. Strange that it compiles fine in 4.4.6 but not in 4.8.2.

error: parse error in template argument list 
Ex: assert(block.begin <  block.end);

works when I parenthesize the block.begin 
Ex. assert( (block.begin) < block.end);

* Paolo Carlini 2015-06-23 22:21:28 UTC *

This should be fixed ASAP, but I'm not actively working on it at the moment.

Community
  • 1
  • 1
lucasg
  • 10,734
  • 4
  • 35
  • 57
0

It appears that g++ 4.8.4 also has problems when the member variable is begin or end.

Using

struct st{
    int begin;
    int end;
};

template <typename T>
void compare(const T& v1, const T& v2){
    if(v1.begin < v2.begin)
    {
        cout << "v1.begin < v2.begin" << endl;
    }
    if(v1.end < v2.end)
    {
        cout << "v1.end < v2.end" << endl;
    }
}

produces the following error message for me.

socc.cc: In function ‘void compare(const T&, const T&)’:
socc.cc:12:11: error: parse error in template argument list
     if(v1.begin < v2.begin)
           ^
socc.cc:16:11: error: parse error in template argument list
     if(v1.end < v2.end)
           ^
socc.cc: In instantiation of ‘void compare(const T&, const T&) [with T = st]’:
socc.cc:25:17:   required from here
socc.cc:12:5: error: ‘begin’ is not a member template function
     if(v1.begin < v2.begin)
     ^
socc.cc:16:5: error: ‘end’ is not a member template function
     if(v1.end < v2.end)

Perhaps someone more familiar with g++ development than me can provide more details.

One work around is to use avoid using begin and end as member variables.

struct st{
    int _begin;
    int _end;
};

or

struct st{
    int _start;
    int _end;
};
R Sahu
  • 204,454
  • 14
  • 159
  • 270
0

I think, for the time being you can check the condition in reverse order.

#include <iostream>
using namespace std;

struct st {
    int start;
    int end;
};

template<typename T>
void compare(const T& v1, const T& v2){
    if(v2.end > v1.end)    //changed the comparison order
        cout << "v1 < v2" << endl;
}

int main() {
    st a = {1, 2};
    st b = {2, 3};
    compare(a, b);
    return 0;
}
spnsp
  • 163
  • 1
  • 8