2017-08-25 4 views
1

Je tente d'implémenter une pile constexpr uniquement pour comprendre constexpr. je reçois une erreur de compilation du code ci-dessous que je ne comprends pas:Gcc 7.2 C++ 17 constexpr

  1. Si je comprends bien constexpr ne signifie pas const
  2. Il compile le constructeur constexpr liste d'initialisation qui contient des appels à pousser
  3. Il compile le spop lambda qui effectue une pop

Que manque-t-il?

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; 
} 

Je reçois cette erreur (je comprends ce que cela signifie, mais pourquoi?)

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() 
         ^~~ 

Répondre

3
  1. Si je comprends bien constexpr ne signifie pas const

N ° Objets déclarés constexprare indeed const. C'est pourquoi dstack2.pop() est mal formé - la raison très ennuyeuse et C++ 03 que vous appelez une fonction membre non const sur un objet const. Une fois la ligne dstack2.pop() supprimée, tout est compilé.

  1. Il compile le constructeur constexpr liste d'initialisation qui contient des appels à pousser
  2. Il compile le SPop lambda qui effectuent une pop

Dans les deux Dans ces cas, vous êtes autorisé à modifier l'objet. Dans le premier cas, vous êtes toujours dans le constructeur - donc les modifications sont bien, l'objet n'est jamais constpendant la construction (sinon vous ne pourriez pas le construire). Dans le cas lambda, l'argument n'est pas const - c'est juste auto.

+0

Merci pour vos explications, mais je ne le point unserstand 3. Il est objet constexpr qu'il est modifié de sorte je n'obtenir le ce qui est différent. de toute façon +1 – Genxers

+3

@Genxers, vérifiez 'dstack.size()' à la fin.Votre lambda modifie une * copie *. – rustyx

+0

@RustyX oui c'est sûr mais c'est fait au moment de la compilation comme vous pouvez le voir sur static_assert. Est-ce un comportement que je ne comprends pas. – Genxers

1

[expr.const]/2:

Une expression e est une expression constante de base à moins que l'évaluation des e, suivant les règles de la machine abstraite, évaluerait l'une des expressions suivantes:

  • [. ..]
  • modification d'un objet à moins qu'il ne soit appliqué à une lvalue non volatile de type littéral qui fait référence à un objet non volatile dont la durée de vie a débuté dans l'évaluation de e;