vsoftco vsoftco - 25 days ago 8
C++ Question

Template dependent base member is not resolved properly

This question is a follow up of
Moving a member function from base class to derived class breaks the program for no obvious reason (this is a prime example of why one shouldn't use

using namespace std;
)

where the answers suggested qualifying by
this->
a dependent template name (which indeed is the way to go when referring to such dependent members). However, there seems to be an issue, so I'll list a minimal example that reproduces the problem.

Consider the code:

#include <iostream>
#include <bitset>

using namespace std;

template<class T>
struct B
{
T bitset{};
};

template<class T>
struct D : B<T>
{
bool foo()
{
return this->bitset < 32;
}
};

int main(){}


Live on Coliru

The perplexing thing is that even though
this->bitset
should refer to the member
B<T>::bitset
, the compiler is still confused and believes that we try to refer to
std::bitset
. The error appears on both gcc6 and clang3.7. Any ideas why this happens? Qualifying it with
B<T>::bitset
works though.

Error (verbatim):


In member function 'bool D<T>::foo(T, std::__cxx11::string)': cpp/scratch/minimal.cpp:24:22: error: invalid use of 'class std::bitset<1ul>'



EDIT

This looks to me like a parsing/name lookup bug. If we replace
<
by any other comparison operator (thanks @Leon for the remark), e.g.

return this->bitset == 32;


the program compiles. So I guess in
this->bitset < 32
the parser believes that we are trying to instantiate a template (the
<
sign), and we forgot to close the
>
. But again have no idea if this is indeed a bug or that's how the language is suppose to work.

Answer

tl;dr it looks like this is a deliberate decision, specifically to support the alternate syntax you already used.

An approximate walkthrough of the standardese below:

this-> B <
         ^
  • this could be either the start of a template id or a less-than, so let's check both!
    1. this->B does name something, but it's a template B<T>, so keep going
    2. B on it's own also names something, a class template B<T>
    3. wait, they're the same thing! That means we're using this->B<T> as a qualifier, and it isn't a less-than after all

In the other case,

this->bitset

proceeds identically until the third step, when it realises there are two different things called bitset (a template class member and a class template), and just gives up.


This is from a working draft I have lying around, so not necessarily the most recent, but:

3.4.5 Class member access [basic.lookup.classref ]

1 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. If the lookup in the class of the object expression finds a template, the name is also looked up in the context of the entire postfix-expression and

  • if the name is not found, the name found in the class of the object expression is used, otherwise
  • if the name is found in the context of the entire postfix-expression and does not name a class template, the name found in the class of the object expression is used, otherwise
  • if the name found is a class template, it shall refer to the same entity as the one found in the class of the object expression, otherwise the program is ill-formed.

So, in any expression like this->id < ..., it has to handle cases where id<... is the start of a template identifier (like this->B<T>::bitset).

It still checks the object first, but if this->id finds a template, further steps apply. And in your case, this->bitset is presumably considered a template as it still depends on T, so it finds the conflicting std::bitset and fails at the third bullet above.

Comments