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

时间:2017-06-05 11:23:59

标签: c++ c++11 using-directives stdarray

在我的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;
  • 如果代码正确,这是GCC7中的错误吗?

1 个答案:

答案 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<

时才应将其视为模板化

是的,在不同的上下文中使用相同的名称是完全正确的,除非它是保留字之一,哪个数组不是。但要小心使用名称。我发现使用名称数组来保存单个整数的变量至少令人困惑。这个名字已经暗示将有不止一个。