Gcc 7.2 c++17 constexpr

Posted

技术标签:

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

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

    如果我正确理解 constexpr 并不意味着 const 它编译包含 push 调用的 init-list constexpr 构造函数 它编译执行 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_type0;
        

        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_type0;
          top_item_ = size_type0;
        

        constexpr array_stack ()
            : items_size_type0, top_item_size_type0, 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_type0;
          rhs.top_item_ = size_type0;
        

        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> dstack0,1,2,3,4,5,6,7,8,9;
  constexpr ds::array_stack <int, 10> dstack2dstack;
  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 ()
                        ^~~

【问题讨论】:

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

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

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

    它编译包含 push 调用的 init-list constexpr 构造函数 它编译执行 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 的评估;

【讨论】:

以上是关于Gcc 7.2 c++17 constexpr的主要内容,如果未能解决你的问题,请参考以下文章

fl 与 gcc 7.2 一起崩溃

编译GCC-7.2时出错

如何在 CentOS 7.2 上使用 yum 安装 gcc 5.3?

升级GCC,支持C++17

C++11/14/17,GCC 7 与 GCC 8:朋友类模板的名称查找

gcc 7.2(Ubuntu)中缺少头文件<execution>:如何解决?