在我的C ++ JSON库中,我最近有一个regression with GCC7。我删除了受影响的代码并希望了解错误。
考虑此标头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 = {};
};
如您所见,我在结构value_t
中使用名称“array”作为成员变量名称,作为静态函数的名称。然后我将标题包含在以下文件中:
#include <array>
using std::array; // note this!
#include "myclass.hpp"
int main()
{}
代码使用GCC6和Clang5编译(使用-std=c++11
),但GCC7报告:
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
似乎解析器将lhs.val.array
中的“数组”作为std::array
读取,并将以下<
视为模板列表的开头。
如果我进行以下任何更改,可以编译代码:
using std::array;
或将其移至#include "myclass.hpp"
。return lhs.val.array < rhs.val.array;
更改为return (lhs.val.array) < rhs.val.array;
。此外,如果删除static A array()
函数...
using std::array;
?答案 0 :(得分:2)
我没有发现任何说明你发现的行为是正常的,但我发现了以下情况,否则可能会断言。
当成员模板专业化的名称出现之后。要么 - &GT;在postfix-expression中或在qualified-id中的嵌套名称说明符之后,postfix-expression的对象表达式是 type-dependent或qualified-id中的嵌套名称说明符引用 到依赖类型,但名称不是当前的成员 实例化(14.6.2.1),成员模板名称必须以前缀为前缀 关键字模板。否则,假定名称命名为a 非模板。
[ 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 ]
您可以通过在括号中放置比较元素来解决已建议的ID。它会破坏名称array<
return (lhs.val.array) < (rhs.val.array);
让我们简化一下你的代码,并删除所有可能掩盖正在发生的事情的包含。我将从仍然无法编译的原始代码开始。
#include <cstddef> // needed for size_t
//using std::array; brings following two lines into your code:
template< class T, std::size_t N >
struct array;
template <typename X>
struct A
{
struct value_t { int array; };
value_t val = {};
friend bool operator<(const A& lhs, const A& rhs) {
return (lhs.val.array < rhs.val.array);
}
};
现在让我们将struct value_t { int array; };
移到模板化定义之外:
#include <cstddef> // needed for size_t
//using std::array; brings following two lines into your code:
template< class T, std::size_t N >
struct array;
struct value_t { int array; };
template <typename X>
struct A
{
value_t val = {};
friend bool operator<(const A& lhs, const A& rhs) {
return (lhs.val.array < rhs.val.array);
}
};
因此,通过在代码中加入<array>
,您可以将模板数组添加到代码中,如下所示。在value_t模板之外的版本中,有array<T>
和成员array
。这些是不同的事情,因此没有任何冲突
当您将value_t放在模板中时,编译器会尝试扩展来自该模板的内容。它尝试使用成员数组执行此操作,该数组不应按标准中的规定进行。
无论如何,它看起来像GCC中的bug,因为当它出现在表达式lhs.val.array
中时,只有在前缀为关键字模板lhs.val.template array<
是的,在不同的上下文中使用相同的名称是完全正确的,除非它是保留字之一,哪个数组不是。但要小心使用名称。我发现使用名称数组来保存单个整数的变量至少令人困惑。这个名字已经暗示将有不止一个。