【问题标题】:Gcc 7.2 c++17 constexprGcc 7.2 c++17 constexpr
【发布时间】:2017-08-25 13:47:36
【问题描述】:

我尝试实现一个 constexpr 堆栈只是为了理解 constexpr。 我从以下代码中得到一个我不理解的编译错误:

  1. 如果我正确理解 constexpr 并不意味着 const
  2. 它编译包含 push 调用的 init-list constexpr 构造函数
  3. 它编译执行 pop 的 lambda spop

我错过了什么?

live example

g++ prog.cc -Wall -Wextra -I/opt/wandbox/boost-1.65.0/gcc-7.2.0/include -std=gnu++1z

#include <array>
#include <stdexcept>
#include <type_traits>

namespace ds {
    template <typename T, std::size_t N>
    class array_stack final {
    public:
        using value_type = T;
        using reference = value_type&;
        using const_reference = value_type const&;
        using size_type = std::size_t;

        constexpr bool empty () const
        {
          return items_ == size_type{0};
        }

        constexpr bool full () const
        {
          return top_item_ == N;
        }

        constexpr size_type size () const
        {
          return items_;
        }

        constexpr reference top () &
        {
          if (empty())
            throw std::logic_error{"Attempting top() on empty stack"};

          return array_[top_item_ - 1];
        }

        constexpr const_reference top () const&
        {
          if (empty())
            throw std::logic_error{"Attempting top() on empty stack"};

          return array_[top_item_ - 1];
        }

        constexpr void push (value_type const& value)
        {
          if (full())
            throw std::logic_error{"Attempting push() on full stack"};

          array_[top_item_] = value;
          top_item_++;
          items_++;
        }

        constexpr void push (value_type&& value)
        {
          if (full())
            throw std::logic_error{"Attempting push() on full stack"};

          array_[top_item_] = std::move(value);
          top_item_++;
          items_++;
        }

        constexpr void pop ()
        {
          if (empty())
            throw std::logic_error{"Attempting pop() on empty stack"};

          top_item_--;
          items_--;
        }

        constexpr void clear ()
        {
          items_ = size_type{0};
          top_item_ = size_type{0};
        }

        constexpr array_stack ()
            : items_{size_type{0}}, top_item_{size_type{0}}, array_{}
        {}

        constexpr array_stack (std::initializer_list<value_type> values) : array_stack ()
        {
          for (auto const& v : values)
            push(v);
        }

        constexpr array_stack (array_stack const& rhs) : array_stack ()
        {
          array_ = rhs.array_;
          items_ = rhs.items_;
          top_item_ = rhs.top_item_;
        }

        constexpr array_stack (array_stack&& rhs)
            : items_ {rhs.items_}, top_item_ {rhs.top_item_}, array_ {std::move(rhs.array_)}
        {
          rhs.items_ = size_type{0};
          rhs.top_item_ = size_type{0};
        }

        constexpr array_stack& operator= (array_stack rhs)
        {
          array_ = std::move(rhs.array_);
          items_ = std::move(rhs.items_);
          top_item_ = std::move(rhs.top_item_);
          return *this;
        }

        ~array_stack () = default;

        void swap (array_stack& rhs) noexcept(std::is_nothrow_swappable_v<value_type>)
        {
          using std::swap;
          swap(items_, rhs.items_);
          swap(top_item_, rhs.top_item_);
          swap(array_, rhs.array_);
        }

    private:
        size_type items_;
        size_type top_item_;
        std::array<value_type, N> array_;
    };

    template <typename T, std::size_t N>
    void swap (array_stack<T, N>& lhs, array_stack<T, N>& rhs) noexcept(noexcept(lhs.swap(rhs)))
    {
        lhs.swap(rhs);
    }
}

constexpr bool f()
{
  constexpr ds::array_stack <int, 10> dstack{0,1,2,3,4,5,6,7,8,9};
  constexpr ds::array_stack <int, 10> dstack2{dstack};
  constexpr auto spop =[](auto s){ s.pop(); return s.size(); };
  static_assert(dstack.size() == 10);
  static_assert(!dstack.empty());
  static_assert(dstack.full());
  static_assert(dstack.top() == 9);
  static_assert(dstack2.size() == 10);
  static_assert(spop(dstack) == 9);
  dstack2.pop();
  return true;
}


int main()
{
  constexpr ds::array_stack <int, 10> cstack;
  static_assert(cstack.size() == 0);
  static_assert(cstack.empty());
  static_assert(!cstack.full());

  static_assert(f());

  return 0;
}

我收到此错误(我明白它的含义,但为什么?)

prog.cc: In function 'constexpr bool f()':
prog.cc:147:15: error: passing 'const ds::array_stack<int, 10>' as 'this' argument discards qualifiers [-fpermissive]
   dstack2.pop();
               ^
prog.cc:66:24: note:   in call to 'constexpr void ds::array_stack<T, N>::pop() [with T = int; long unsigned int N = 10]'
         constexpr void pop ()
                        ^~~

【问题讨论】:

    标签: c++ gcc compiler-errors c++17


    【解决方案1】:
    1. 如果我正确理解 constexpr 并不意味着 const

    没有。对象声明为constexprare indeed const。这就是为什么 dstack2.pop() 格式不正确的原因 - 非常无聊和 C++03 原因,您在 const 对象上调用非 const 成员函数。

    删除dstack2.pop() 行后,一切都会编译。

    1. 它编译包含 push 调用的 init-list constexpr 构造函数
    2. 它编译执行 pop 的 lambda spop

    在这两种情况下,您都可以修改对象。在第一种情况下,您仍然在构造函数中 - 所以修改很好,对象永远不会是 const during 构造(否则您无法构造它)。在 lambda 情况下,参数不是 const - 它只是 auto

    【讨论】:

    • 感谢您的解释,但我不理解第 3 点。它是 constexpr 对象,它已被修改,所以我不明白有什么不同。无论如何+1
    • @Genxers,最后检查dstack.size()。你的 lambda 修改了一个 copy.
    • @RustyX 是肯定的,但在编译时完成,正如您从 static_assert 中看到的那样。这是我不明白的行为吗?
    • 我终于明白了... C++11,constexpr 成员函数是隐式 const。其次,它们有 void 返回类型,而 void 在 C++11 中不是文字类型。这两个限制都在 C++14 中从 S.Meyers 的书 Eff modern c++ 中解除
    【解决方案2】:

    [expr.const]/2:

    表达式 e 是核心常量表达式,除非根据抽象机的规则对 e 的求值将求值以下表达式之一:

    • [...]
    • 对象的修改,除非将其应用于文字类型的非易失性左值,该左值引用一个非易失性对象,该对象的生命周期始于对 e 的评估;

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-10-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-09-10
      • 1970-01-01
      相关资源
      最近更新 更多