【问题标题】:Constexpr find implementationconstexpr 查找实现
【发布时间】:2015-09-04 12:13:00
【问题描述】:

在回答this question 并阅读this talk 并查看this code 之后,我想用简单的数组类来实现constexpr find

考虑以下示例:

#include <cstddef>

template <class It, class T>
constexpr auto constexpr_find(const It& b, const It& e, T value) {
    auto begin = b;
    while (begin != e) {
        if (*begin == value) break;

        ++begin;
    }
    return *begin;
}

template<typename T, size_t N>
class array
{
public:
   typedef T* iterator;
   typedef const T* const_iterator;
   constexpr auto begin() const { return const_iterator(array_); }
   constexpr auto end() const { return const_iterator(array_ + N); }

   T array_[N];
   static constexpr size_t size = N;
};

int main()
{
   constexpr array<int, 3> array{{0,2,3}};
   static_assert(constexpr_find(array.begin(), array.end(), 0) == 0, "");
}

compiles as expected

并使用自定义 constexpr 迭代器:

template<class T>
class array_iterator
{
public:
   constexpr array_iterator(const T* v) : iterator(v)
   {
   }
   constexpr const T& operator * () const { return *iterator; }
   constexpr array_iterator& operator ++()
   {
      ++iterator;
      return *this;
   }
   constexpr bool operator != (const array_iterator& other) const { return iterator != other.iterator; }
private:
   const T* iterator;
};

在数组类中:

typedef const array_iterator<const T> const_iterator;

这是唯一的区别,编译器给我错误:

在 constexpr 展开 constexpr_find<array_iterator<const int>, int>(array.array<T, N>::begin<int, 3u>(), array.array<T, N>::end<int, 3u>(), 0)

错误:(((const int*)(& array.array<int, 3u>::array_)) != (((const int*)(& array.array<int, 3u>::array_)) + 12u)) 不是常量 表达

Live example

这是gcc的bug,因为clang编译这么好,还是两个sn-ps有区别?

【问题讨论】:

  • [OT]:在constexpr_find 中,当您返回元素而不是迭代器时,您不会管理元素不存在的情况。
  • @Jarod42 谢谢,我知道。这只是 constexpr_additions 提案中的一个示例。
  • boost::mpl 是高潮

标签: c++ c++14 constexpr


【解决方案1】:

我不能肯定地说,但是您将数组成员的指针存储到外部迭代器类中,这可能是导致该错误的原因。

---------更新开始---------

这是演示问题的最小 sn-p:

constexpr const struct A { int i[2]; } a {{0,0}};

int main ()
{
  static_assert (nullptr != a.i  , ""); // ok
  static_assert (nullptr != a.i+0, ""); // ok
  static_assert (nullptr != a.i+1, ""); // error
}

似乎禁止在常量表达式中使用指向数组元素(偏移量非零)的指针。

------更新结束---------

解决方法很简单 - 存储指向数组对象和偏移量的指针。

Live

#include <cstddef>

template <class It, class T>
constexpr auto constexpr_find(const It& b, const It& e, T value) {
    auto begin = b, end = e;
    while (begin != end) {
        if (*begin == value) break;

        ++begin;
    }
    return *begin;
}

template<class Array>
class array_iterator
{
public:
   constexpr array_iterator(const Array& a, size_t pos=0u) : array_(&a), pos_ (pos)
   {
   }
   constexpr const typename Array::value_type& 
   operator * () const { return (*array_)[pos_]; }

    constexpr array_iterator& operator ++()
   {
      ++pos_;
      return *this;
   }
   constexpr bool operator != (const array_iterator& other) const 
   { return array_ != other.array_ || pos_ != other.pos_; }

private:
   const Array* array_;
   size_t pos_;
};

template<typename T, size_t N>
class array
{
public:
   typedef T value_type;
   typedef const array_iterator<array> const_iterator;
   constexpr T const& operator[] (size_t idx) const { return array_[idx]; }
   constexpr auto begin() const { return const_iterator(*this); }
   constexpr auto end() const { return const_iterator(*this, N); }

   T array_[N];
   static constexpr size_t size = N;
};

int main()
{
   constexpr array<int, 3> array{{0,2,3}};
   static_assert(constexpr_find(array.begin(), array.end(), 0) == 0, "");
}

顺便说一句,可以实现C++11版本的constexpr启用find:

Live

#include <cstddef>
#include <cassert>

#if !defined(__clang__) && __GNUC__ < 5
// TODO: constexpr asserts does not work in gcc4, but we may use 
// "thow" workaround from 
// http://ericniebler.com/2014/09/27/assert-and-constexpr-in-cxx11/
# define ce_assert(x) ((void)0)
#else
# define ce_assert(x) assert(x)
#endif
namespace my {

template <class It, class T>
inline constexpr It
find (It begin, It end, T const& value) noexcept
{
    return ! (begin != end && *begin != value)
         ? begin
         : find (begin+1, end, value);
}

template<class Array>
class array_iterator
{
public:
  using value_type = typename Array::value_type;

  constexpr array_iterator(const Array& array, size_t size = 0u) noexcept
    : array_ (&array)
    , pos_ (size)
  {}

  constexpr const value_type operator* () const noexcept
  {
    return ce_assert (pos_ < Array::size), (*array_) [pos_];
  }

#if __cplusplus >= 201402L // C++14
  constexpr
#endif
  array_iterator& operator ++() noexcept
  {
    return ce_assert (pos_ < Array::size), ++pos_, *this;
  }

  constexpr array_iterator operator+ (size_t n) const noexcept
  {
    return ce_assert (pos_+n <= Array::size), array_iterator (*array_, pos_+n);
  }

  friend constexpr bool
  operator != (const array_iterator& i1, const array_iterator& i2) noexcept
  {
    return i1.array_ != i2.array_ || i1.pos_ != i2.pos_;
  }

  friend constexpr size_t
  operator- (array_iterator const& i1, array_iterator const& i2) noexcept
  {
    return ce_assert (i1.array_ == i2.array_), i1.pos_ - i2.pos_;
  }

private:
  const Array* array_;
  size_t pos_;
};

template<typename T, size_t N>
class array
{
public:
  using value_type = T;
  using const_iterator = const array_iterator<array>;

  constexpr value_type const&
  operator[] (size_t idx) const noexcept
  { return array_[idx];  }

  constexpr const_iterator begin() const noexcept
  { return const_iterator(*this); }

  constexpr const_iterator end()   const noexcept
  { return const_iterator(*this, N); }

  T array_[N];
  static constexpr size_t size = N;
};

}

int main()
{
  static constexpr my::array<int, 3> array{{0,2,3}};

  static_assert (
    find (array.begin(), array.end(), 2) - array.begin () == 1,
    "error");
}

您可能也有兴趣查看Sprout library,它包含很多 constexpr 数据结构和算法。

【讨论】:

  • 感谢您的解决方法,但我不需要 C++11 constexpr find。顺便说一句,这是 gcc 错误,因此,使用 gcc 代码只能使用解决方法。
  • @ForEveR:我不确定这是否是一个错误。可能是clang中的一些clang语言扩展或宽松的std实现。我更新了我的答案并添加了非常少的代码 sn-p 触发 gcc 中的编译错误。
  • 可能是...但无论如何相关:gcc.gnu.org/bugzilla/show_bug.cgi?id=67376 可能是错误,也可能不是。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-24
  • 1970-01-01
  • 1970-01-01
  • 2016-01-23
  • 1970-01-01
相关资源
最近更新 更多