【问题标题】:Is array indexing constexpr or not ? GCC inconsistent?数组索引是否为 constexpr ? GCC 不一致?
【发布时间】:2014-03-13 22:53:36
【问题描述】:

在他对我问题的回答中 Avoiding struct in variadic template function iavr 指出“std::array::operator[] 仅在 C++14 中为 constexpr”。我的 这里的问题是确保 GCC 行为不一致,而事实并非如此 我错误地理解了标准。

我正在探索使用一些模板元编程的不同方式 将二维数组初始化为帕斯卡三角形(外部为 0)。 在我在这里尝试的那个中,我想尽可能避免使用 模板结构,特别是可变参数,如果 constexpr 函数 和数组。

匆忙的读者注意:为了方便起见,我放了以下三段代码 完整性,但您不需要了解它们。


我使用以下两个非常标准的定义:

template <typename... Ts> struct Sequence {};
template<unsigned N, unsigned... Is> struct Range {
    typedef typename Range<N-1, N-1, Is...>::type type;
};
template<unsigned... Is> struct Range<0, Is...> {
    typedef Sequence<std::integral_constant<unsigned int, Is>...> type;
};

然后我有以下模板 constexpr 函数,它给出了一行 三角形,计算下一个:

// nextline
template <typename... SeqTis, typename T, size_t Size>
constexpr std::array<T, Size>
nextline(Sequence<SeqTis...>, const typename std::array<T, Size> ar) {
  return { 1, (ar[SeqTis::value]+ar[SeqTis::value+1])... };
}
template <typename T, size_t Size>
constexpr std::array<T, Size>
nextline(const typename std::array<T, Size> ar) {
  return nextline(typename Range<Size-1>::type(), ar);
}

然后在部分初始化的数组末尾添加一个元素:

template <typename... SeqTis, typename T, size_t Size>
constexpr std::array<T, Size>
appendarray(Sequence<SeqTis...>, const typename std::array<T, Size> ar, const T el) {
  return { ar[SeqTis::value]..., el };
}
template <size_t Pos, typename T, size_t Size>
constexpr std::array<T, Size>
appendarray(const typename std::array<T, Size> ar, const T el) {
  return appendarray(typename Range<Pos>::type(), ar, el);
}

在这些代码中,我使用了数组索引,它非常有效。你可以试试 与:

constexpr auto ar0 = std::array<int, 3> { 1,0,0 };
constexpr auto ar1 = nextline(ar0);
constexpr auto ar2 = appendarray<2>(ar1, 12);
for (auto i: ar2) std::cout << i << " "; // prints 1 1 12

但是当我尝试编译以下递归结构时:

template <typename T, size_t N>
using Ar2  = std::array<std::array<T, N+1>, N+1>;

template<typename T, size_t N, size_t l> struct Loop {
  constexpr static Ar2<T, N> next() {
    return appendarray<l>(Loop<T, N, l-1>::next(),
                  nextline(Loop<T, N, l-1>::next()[l-1]));
  }
};

template<typename T, size_t N> struct Loop<T, N, 0> {
  constexpr static Ar2<T, N> next() {
    return Ar2<T, N>({ {1, 0} });
  }
};

};

然后 GCC 抱怨

[...]
binom2.cpp:48:30: note: ‘static constexpr Ar2<T, N> Loop<T, N, l>::next() [with T = long long int; long unsigned int N = 10ul; long unsigned int l = 10ul; Ar2<T, N> = std::array<std::array<long long int, 11ul>, 11ul>]’ is not usable as a constexpr function because:

constexpr static Ar2<T, N> next() {
                              ^
binom2.cpp:50:38: error: call to non-constexpr function ‘std::array<_Tp, _Nm>::value_type& std::array<_Tp, _Nm>::operator[](std::array<_Tp, _Nm>::size_type) [with _Tp = std::array<long long int, 11ul>; long unsigned int _Nm = 11ul; std::array<_Tp, _Nm>::reference = std::array<long long int, 11ul>&; std::array<_Tp, _Nm>::value_type = std::array<long long int, 11ul>; std::array<_Tp, _Nm>::size_type = long unsigned int]’
  nextline(Loop<T, N, l-1>::next()[l-1]));

似乎有时 GCC 允许 constexpr 数组索引,有时它 没有。我是不是错过了什么。

【问题讨论】:

  • 据我了解,T&amp; std::array&lt;T,N&gt;::operator[] 不是 constexpr,但 constexpr const T&amp; std::array&lt;T,N&gt;::operator[] const 是...您必须手动转换任何返回 std::array 的内容 (const std ::array&)
  • 那么看来 GCC 应该接受我的代码,因为 Loop&lt;...&gt;::next() 是 constexpr。
  • 试试nextline(((const std::array&lt;T,N&gt;&amp;)Loop&lt;T, N, l-1&gt;::next())[l-1])。 nextline 返回一个非常量 std::array,因此您需要将其强制转换为 const 数组。
  • @hivert:至少在 C++14 中,constexpr 并不意味着 const
  • std::get&lt;N&gt;(std::array&lt;..&gt;&amp;)constexpr(在 C++1y 中)并返回一个非常量引用。不知道为什么 std::array::operator[] 在非常量情况下不是 constexpr。

标签: c++ arrays gcc c++11 constexpr


【解决方案1】:

据我了解,

T& std::array<T,N>::operator[]

不是constexpr,而是

 constexpr const T& std::array<T,N>::operator[] const

是...您必须手动转换任何返回(非常量)std::array 和 (const std::array&) 的内容,以使其选择正确的运算符。

在对强制转换语法进行了一些修改后,看起来正确的语法是:

nextline(((const Ar2<T, N>&) Loop<T, N, l-1>::next())[l-1])

尽管您可能仍想查看 const_cast,因为常规转换会删除函数调用的右值性。

【讨论】:

  • 您可能会澄清 constexpr operator[] for array 在 C++1y 中是标准的,但是对 C++11 中指定的行为的扩展(因此不可移植)。在 C++11 中,sizemax_sizeempty 是唯一需要为 array 的成员 constexpr(根据 23.2.1 [array.overview]/3)。
  • @dyp T&amp;const&amp;&amp; 会减少到 T const&amp; 吗?
  • @Yakk No,但他确实说过“类似”;)
  • 顺便用const_cast代替C cast
  • 我可以做到as_const with 2 overloads。有人找到更简单的实现吗?
【解决方案2】:

最简单的解决方法是声明next 函数以返回一个 constexpr const 数组:

template<typename T, size_t N, size_t l> struct Loop {
  constexpr const static Ar2<T, N> next() {
    return appendarray<l>(Loop<T, N, l-1>::next(),
                  nextline(Loop<T, N, l-1>::next()[l-1]) );
  }
};

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-04-02
    • 1970-01-01
    • 2016-08-20
    • 1970-01-01
    • 1970-01-01
    • 2015-07-04
    • 2010-10-22
    相关资源
    最近更新 更多