In my C++ JSON library, I recently had a regression with GCC7. I stripped down the affected code and hope to understand the error.
The code
Consider this header myclass.hpp
:
#pragma once
template <typename X>
struct A
{
struct value_t
{
X array;
};
static A array()
{
return A();
}
friend bool operator<(const A& lhs, const A& rhs) noexcept
{
return lhs.val.array < rhs.val.array;
}
value_t val = {};
};
As you see, I used the name "array" as member variable name in struct value_t
, as name of a static function. I then included the header in the following file:
#include <array>
using std::array; // note this!
#include "myclass.hpp"
int main()
{}
The problem
The code compiles with GCC6 and Clang5 (using -std=c++11
), but GCC7 reports:
In file included from example.cpp:3:0:
myclass.hpp: In function 'bool operator<(const A<X>&, const A<X>&)':
myclass.hpp:19:40: error: wrong number of template arguments (1, should be 2)
return lhs.val.array < rhs.val.array;
^~~~~
In file included from example.cpp:1:0:
/usr/local/Cellar/gcc/7.1.0/include/c++/7.1.0/array:94:12: note: provided for 'template<class _Tp, long unsigned int _Nm> struct std::array'
struct array
^~~~~
make: *** [all] Error 1
It seems as if the parser reads the "array" in lhs.val.array
as std::array
and treats the following <
as the start of a template list.
The code can be compiled if I make any of the changes below:
- Remove the
using std::array;
or move it behind#include "myclass.hpp"
. - Change
return lhs.val.array < rhs.val.array;
toreturn (lhs.val.array) < rhs.val.array;
.
In addition, either compiler fails if I remove the static A array()
function...
My questions
- Is the code correct in the first place? Am I allowed to use "array" as a name even if I use
using std::array;
? - If the code is correct, is this a bug in GCC7?
return (lhs.val.array) < rhs.val.array;
. It compiles just fine with GCC7 and GCC8 as well. – Shodunordered_map
as a name, it fails both with GCC6 and clang5. Therefore nothing to do with the compiler too. – Shod(
and)
). – Shodfriend
delayed compilation... same problem withbool f(
instead offriend bool operator<(
, and same problem if the function body is given out-of-line – Phagolhs.val
has type unknown specialization (since the type oflhs
is a dependent type); there are a lot of restrictions on the use of unknown specialization, with violations being ill-formed NDR. I'm not yet sure if this specific code is such a violation though – Phagotemplate
, the compiler must assume the name refers to a non-template. But I can't find anything talking about the case where there isn't a member template of the same name, but there might be due to it being of unknown specialization. (BTW this may be why the member functionarray
makes a difference in some compilers) – Phago(x)<
cannot be parsed as introducing a template (the<
must immediately follow an id-expression to be a template) – Phagostd::array
first, and since it sees the<
, it is interpretinglhs.val.array <
as an attempted template instantiation. When in fact what should be happening is that the compiler sees the postfix expressionlhs.val.
as class member access (expr.ref), so that the id expressionarray
can be interpreted as a member of the class. +1 for Microsoft this time around as it doesn't have the same issue gcc or clang do. – Mattahlhs.val
a member of the current instantiation? – Asphodel