使用 std::array 并使用“array”作为名称

Using std::array and using "array" as name

提问人:Niels Lohmann 提问时间:6/5/2017 更新时间:6/20/2017 访问量:1321

问:

在我的 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.arraystd::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 中的错误吗?
C++ C++11 使用指令 stdarray

评论

1赞 6/5/2017
我会选择比“数组”更具描述性的东西作为变量名称。
7赞 skypjack 6/5/2017
它看起来很像 GCC 错误,但您可以通过以下方式轻松解决它:.它也可以很好地编译 GCC7 和 GCC8。return (lhs.val.array) < rhs.val.array;
1赞 skypjack 6/5/2017
@Peter 可重现至 GCC8 快照。
3赞 M.M 6/5/2017
简化的 MCVE(与 std::array 无关)
3赞 T.C. 6/6/2017
比较核心问题 1835

答:

4赞 Marek Vitek 6/20/2017 #1

我没有发现任何表明您发现的行为是可以的,但是我发现以下内容可能是其他断言。

当成员模板专用化的名称出现在 之后时。或 -> 在 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.arraylhs.val.template array<

是的,在不同的上下文中使用相同的名称是完全正确的,除非它是保留字之一,而该数组不是。但要小心这种名称的使用。我发现将名称数组用于包含单个整数的变量至少令人困惑。这个名字已经表明会有不止一个。