提问人:Niels Lohmann 提问时间:6/5/2017 更新时间:6/20/2017 访问量:1321
使用 std::array 并使用“array”作为名称
Using std::array and using "array" as name
问:
在我的 C++ JSON 库中,我最近对 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 = {};
};
如您所见,我在 struct 中使用了名称“array”作为成员变量名称,作为静态函数的名称。然后,我将标头包含在以下文件中:value_t
#include <array>
using std::array; // note this!
#include "myclass.hpp"
int main()
{}
问题
该代码使用 GCC6 和 Clang5(使用 )进行编译,但 GCC7 报告:-std=c++11
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
似乎解析器将“数组”读入 as 并将以下内容视为模板列表的开头。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;
- 如果代码正确,这是 GCC7 中的错误吗?
答:
我没有发现任何表明您发现的行为是可以的,但是我发现以下内容可能是其他断言。
当成员模板专用化的名称出现在 之后时。或 -> 在 postfix-expression 中或在 qualified-id 中的 nested-name-specifier 之后,并且 postfix-expression 的对象表达式是 依赖于类型或 qualified-id 中的 nested-name-specifier 引用 设置为依赖类型,但名称不是当前 实例化 (14.6.2.1),成员模板名称必须以 关键字模板。否则,该名称将假定为命名 非模板。
[ 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);
}
};
因此,通过包含在代码中,您将模板数组引入到代码中,如下所示。在模板之外具有value_t的版本中,有 和 成员 。这些是不同的东西,因此没有任何冲突。
当您将value_t放入模板中时,编译器将开始尝试扩展来自该模板的内容。它尝试使用成员数组来执行此操作,而成员数组不应按照标准中的规定发生。<array>
array<T>
array
无论如何,它看起来像 GCC 中的错误,因为当它出现在表达式中时,只有当它以关键字 template 为前缀时,它才应该被视为模板化lhs.val.array
lhs.val.template array<
是的,在不同的上下文中使用相同的名称是完全正确的,除非它是保留字之一,而该数组不是。但要小心这种名称的使用。我发现将名称数组用于包含单个整数的变量至少令人困惑。这个名字已经表明会有不止一个。
评论
return (lhs.val.array) < rhs.val.array;