Implementation of static_vector using an array of std::aligned_storage, with std::launder and forwarding












1















I'm trying to expand on the implementation of static_vector on the std::aligned_storage reference page, but would like to split it into two parts. First, an aligned_storage_array that supports perfect forwarding (so that I can emplace into it) and doesn't require a default constructor, and then the actual static_vector infrastructure that builds upon it. This will let me use the aligned_storage_array for some other static data structures I plan to make in the future.



aligned_storage_array.h



#pragma once

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

namespace nonstd
{
template<class T, std::size_t N>
struct aligned_storage_array
{
public:
aligned_storage_array() = default;
~aligned_storage_array() = default;

aligned_storage_array(aligned_storage_array&& rhs)
//requires std::is_move_constructible_v<T>
= default;

aligned_storage_array& operator=(aligned_storage_array&& rhs)
//requires std::is_move_assignable_v<T>
= default;

aligned_storage_array(const aligned_storage_array& rhs)
//requires std::is_copy_constructible_v<T>
= default;

aligned_storage_array& operator=(const aligned_storage_array& rhs)
//requires std::is_copy_assignable_v<T>
= default;

// Size
constexpr std::size_t size() const noexcept { return N; }
constexpr std::size_t max_size() const noexcept { return N; }

// Access
inline T& operator(std::size_t pos)
{
return *std::launder(
reinterpret_cast<T*>(
std::addressof(m_data[pos])));
}

inline const T& operator(std::size_t pos) const
{
return *std::launder(
reinterpret_cast<const T*>(
std::addressof(m_data[pos])));
}

inline T& at(std::size_t pos)
{
return *std::launder(
reinterpret_cast<T*>(
std::addressof(m_data.at(pos))));
}

inline const T& at(std::size_t pos) const
{
return *std::launder(
reinterpret_cast<const T*>(
std::addressof(m_data.at(pos))));
}

// Operations
template<typename ...Args>
inline T& emplace(size_t pos, Args&&... args)
{
return
*::new(std::addressof(m_data[pos]))
T(std::forward<Args>(args)...);
}

template<typename ...Args>
inline T& bounded_emplace(size_t pos, Args&&... args)
{
return
*::new(std::addressof(m_data.at(pos)))
T(std::forward<Args>(args)...);
}

inline void destroy(std::size_t pos)
{
std::destroy_at(
std::launder(
reinterpret_cast<const T*>(
std::addressof(m_data[pos]))));
}

inline void bounded_destroy(std::size_t pos)
{
std::destroy_at(
std::launder(
reinterpret_cast<const T*>(
std::addressof(m_data.at(pos)))));
}

private:
std::array<std::aligned_storage_t<sizeof(T), alignof(T)>, N> m_data;
};
}


static_vector.h



#pragma once

#include <array>
#include <stdexcept>

#include "aligned_storage_array.h"

namespace nonstd
{
template<class T, std::size_t N>
struct static_vector
{
public:
using value_type = T;
using pointer = T*;
using const_pointer = const T*;
using reference = value_type&;
using const_reference = const value_type&;
using iterator = value_type*;
using const_iterator = const value_type*;
using size_type = std::size_t;

static_vector() = default;
~static_vector() { destroy_n(m_size); }

static_vector(static_vector&& rhs) = default;
static_vector& operator=(static_vector&& rhs) = default;
static_vector(const static_vector& rhs) = default;
static_vector& operator=(const static_vector& rhs) = default;

// Size and capacity
constexpr std::size_t size() const { return m_size; }
constexpr std::size_t max_size() const { return N; }
constexpr bool empty() const { return m_size == 0; }

// Iterators
inline iterator begin() { return &m_data[0]; }
inline const_iterator begin() const { return &m_data[0]; }
inline iterator end() { return &m_data[m_size]; }
inline const_iterator end() const { return &m_data[m_size]; }

// Access
inline T& operator(std::size_t pos)
{
return m_data[pos];
}

inline const T& operator(std::size_t pos) const
{
return m_data[pos];
}

inline T& at(std::size_t pos)
{
if (pos >= m_size)
throw std::out_of_range("static_vector subscript out of range");
return m_data.at(pos);
}

inline const T& at(std::size_t pos) const
{
if (pos >= m_size)
throw std::out_of_range("static_vector subscript out of range");
return m_data.at(pos);
}

// Operations
template<typename ...Args>
inline T& emplace_back(Args&&... args)
{
T& result = m_data.bounded_emplace(m_size, args...);
++m_size;
return result;
}

inline void clear()
{
std::size_t count = m_size;
m_size = 0; // In case of exception
destroy_n(count);
}

private:
void destroy_n(std::size_t count)
{
for (std::size_t pos = 0; pos < count; ++pos)
m_data.destroy(pos);
}

aligned_storage_array<T, N> m_data;
std::size_t m_size = 0;
};
}


A full functional test setup is available here (wandbox). The concept lines can be uncommented and will parse in a compiler that supports them. Mostly I'd like some extra eyes to help determine:




  1. Is this actually safe for placement new with respect to alignment?

  2. Is the use of std::launder correct?

  3. Is the use of reinterpret_cast correct (or should it be two static_casts instead?)

  4. Are there any hidden pitfalls I should watch out for here (aside from the maximum capacity)?

  5. Am I being sufficiently paranoid (::new, std::address_of, std::destroy_at)? Any other safety features I can put in to handle risky operator overloads?

  6. How should I actually handle copy and move? The aligned_storage will happily copy irrespective of whether or not T actually has copy and move functions. I don't think these concepts are enough because I'm not calling the actual constructors or operators. However in the array base I don't know which entries are and aren't valid to copy/move.


I'm told this is similar to something like boost::small_vector, but I want the aligned_storage_array generalized because I want to use it for a number of different static structures later. Also I would like to learn more about alignment/placement new, forwarding, and launder.



Thank you!









share







New contributor




rtek is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.

























    1















    I'm trying to expand on the implementation of static_vector on the std::aligned_storage reference page, but would like to split it into two parts. First, an aligned_storage_array that supports perfect forwarding (so that I can emplace into it) and doesn't require a default constructor, and then the actual static_vector infrastructure that builds upon it. This will let me use the aligned_storage_array for some other static data structures I plan to make in the future.



    aligned_storage_array.h



    #pragma once

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

    namespace nonstd
    {
    template<class T, std::size_t N>
    struct aligned_storage_array
    {
    public:
    aligned_storage_array() = default;
    ~aligned_storage_array() = default;

    aligned_storage_array(aligned_storage_array&& rhs)
    //requires std::is_move_constructible_v<T>
    = default;

    aligned_storage_array& operator=(aligned_storage_array&& rhs)
    //requires std::is_move_assignable_v<T>
    = default;

    aligned_storage_array(const aligned_storage_array& rhs)
    //requires std::is_copy_constructible_v<T>
    = default;

    aligned_storage_array& operator=(const aligned_storage_array& rhs)
    //requires std::is_copy_assignable_v<T>
    = default;

    // Size
    constexpr std::size_t size() const noexcept { return N; }
    constexpr std::size_t max_size() const noexcept { return N; }

    // Access
    inline T& operator(std::size_t pos)
    {
    return *std::launder(
    reinterpret_cast<T*>(
    std::addressof(m_data[pos])));
    }

    inline const T& operator(std::size_t pos) const
    {
    return *std::launder(
    reinterpret_cast<const T*>(
    std::addressof(m_data[pos])));
    }

    inline T& at(std::size_t pos)
    {
    return *std::launder(
    reinterpret_cast<T*>(
    std::addressof(m_data.at(pos))));
    }

    inline const T& at(std::size_t pos) const
    {
    return *std::launder(
    reinterpret_cast<const T*>(
    std::addressof(m_data.at(pos))));
    }

    // Operations
    template<typename ...Args>
    inline T& emplace(size_t pos, Args&&... args)
    {
    return
    *::new(std::addressof(m_data[pos]))
    T(std::forward<Args>(args)...);
    }

    template<typename ...Args>
    inline T& bounded_emplace(size_t pos, Args&&... args)
    {
    return
    *::new(std::addressof(m_data.at(pos)))
    T(std::forward<Args>(args)...);
    }

    inline void destroy(std::size_t pos)
    {
    std::destroy_at(
    std::launder(
    reinterpret_cast<const T*>(
    std::addressof(m_data[pos]))));
    }

    inline void bounded_destroy(std::size_t pos)
    {
    std::destroy_at(
    std::launder(
    reinterpret_cast<const T*>(
    std::addressof(m_data.at(pos)))));
    }

    private:
    std::array<std::aligned_storage_t<sizeof(T), alignof(T)>, N> m_data;
    };
    }


    static_vector.h



    #pragma once

    #include <array>
    #include <stdexcept>

    #include "aligned_storage_array.h"

    namespace nonstd
    {
    template<class T, std::size_t N>
    struct static_vector
    {
    public:
    using value_type = T;
    using pointer = T*;
    using const_pointer = const T*;
    using reference = value_type&;
    using const_reference = const value_type&;
    using iterator = value_type*;
    using const_iterator = const value_type*;
    using size_type = std::size_t;

    static_vector() = default;
    ~static_vector() { destroy_n(m_size); }

    static_vector(static_vector&& rhs) = default;
    static_vector& operator=(static_vector&& rhs) = default;
    static_vector(const static_vector& rhs) = default;
    static_vector& operator=(const static_vector& rhs) = default;

    // Size and capacity
    constexpr std::size_t size() const { return m_size; }
    constexpr std::size_t max_size() const { return N; }
    constexpr bool empty() const { return m_size == 0; }

    // Iterators
    inline iterator begin() { return &m_data[0]; }
    inline const_iterator begin() const { return &m_data[0]; }
    inline iterator end() { return &m_data[m_size]; }
    inline const_iterator end() const { return &m_data[m_size]; }

    // Access
    inline T& operator(std::size_t pos)
    {
    return m_data[pos];
    }

    inline const T& operator(std::size_t pos) const
    {
    return m_data[pos];
    }

    inline T& at(std::size_t pos)
    {
    if (pos >= m_size)
    throw std::out_of_range("static_vector subscript out of range");
    return m_data.at(pos);
    }

    inline const T& at(std::size_t pos) const
    {
    if (pos >= m_size)
    throw std::out_of_range("static_vector subscript out of range");
    return m_data.at(pos);
    }

    // Operations
    template<typename ...Args>
    inline T& emplace_back(Args&&... args)
    {
    T& result = m_data.bounded_emplace(m_size, args...);
    ++m_size;
    return result;
    }

    inline void clear()
    {
    std::size_t count = m_size;
    m_size = 0; // In case of exception
    destroy_n(count);
    }

    private:
    void destroy_n(std::size_t count)
    {
    for (std::size_t pos = 0; pos < count; ++pos)
    m_data.destroy(pos);
    }

    aligned_storage_array<T, N> m_data;
    std::size_t m_size = 0;
    };
    }


    A full functional test setup is available here (wandbox). The concept lines can be uncommented and will parse in a compiler that supports them. Mostly I'd like some extra eyes to help determine:




    1. Is this actually safe for placement new with respect to alignment?

    2. Is the use of std::launder correct?

    3. Is the use of reinterpret_cast correct (or should it be two static_casts instead?)

    4. Are there any hidden pitfalls I should watch out for here (aside from the maximum capacity)?

    5. Am I being sufficiently paranoid (::new, std::address_of, std::destroy_at)? Any other safety features I can put in to handle risky operator overloads?

    6. How should I actually handle copy and move? The aligned_storage will happily copy irrespective of whether or not T actually has copy and move functions. I don't think these concepts are enough because I'm not calling the actual constructors or operators. However in the array base I don't know which entries are and aren't valid to copy/move.


    I'm told this is similar to something like boost::small_vector, but I want the aligned_storage_array generalized because I want to use it for a number of different static structures later. Also I would like to learn more about alignment/placement new, forwarding, and launder.



    Thank you!









    share







    New contributor




    rtek is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
    Check out our Code of Conduct.























      1












      1








      1








      I'm trying to expand on the implementation of static_vector on the std::aligned_storage reference page, but would like to split it into two parts. First, an aligned_storage_array that supports perfect forwarding (so that I can emplace into it) and doesn't require a default constructor, and then the actual static_vector infrastructure that builds upon it. This will let me use the aligned_storage_array for some other static data structures I plan to make in the future.



      aligned_storage_array.h



      #pragma once

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

      namespace nonstd
      {
      template<class T, std::size_t N>
      struct aligned_storage_array
      {
      public:
      aligned_storage_array() = default;
      ~aligned_storage_array() = default;

      aligned_storage_array(aligned_storage_array&& rhs)
      //requires std::is_move_constructible_v<T>
      = default;

      aligned_storage_array& operator=(aligned_storage_array&& rhs)
      //requires std::is_move_assignable_v<T>
      = default;

      aligned_storage_array(const aligned_storage_array& rhs)
      //requires std::is_copy_constructible_v<T>
      = default;

      aligned_storage_array& operator=(const aligned_storage_array& rhs)
      //requires std::is_copy_assignable_v<T>
      = default;

      // Size
      constexpr std::size_t size() const noexcept { return N; }
      constexpr std::size_t max_size() const noexcept { return N; }

      // Access
      inline T& operator(std::size_t pos)
      {
      return *std::launder(
      reinterpret_cast<T*>(
      std::addressof(m_data[pos])));
      }

      inline const T& operator(std::size_t pos) const
      {
      return *std::launder(
      reinterpret_cast<const T*>(
      std::addressof(m_data[pos])));
      }

      inline T& at(std::size_t pos)
      {
      return *std::launder(
      reinterpret_cast<T*>(
      std::addressof(m_data.at(pos))));
      }

      inline const T& at(std::size_t pos) const
      {
      return *std::launder(
      reinterpret_cast<const T*>(
      std::addressof(m_data.at(pos))));
      }

      // Operations
      template<typename ...Args>
      inline T& emplace(size_t pos, Args&&... args)
      {
      return
      *::new(std::addressof(m_data[pos]))
      T(std::forward<Args>(args)...);
      }

      template<typename ...Args>
      inline T& bounded_emplace(size_t pos, Args&&... args)
      {
      return
      *::new(std::addressof(m_data.at(pos)))
      T(std::forward<Args>(args)...);
      }

      inline void destroy(std::size_t pos)
      {
      std::destroy_at(
      std::launder(
      reinterpret_cast<const T*>(
      std::addressof(m_data[pos]))));
      }

      inline void bounded_destroy(std::size_t pos)
      {
      std::destroy_at(
      std::launder(
      reinterpret_cast<const T*>(
      std::addressof(m_data.at(pos)))));
      }

      private:
      std::array<std::aligned_storage_t<sizeof(T), alignof(T)>, N> m_data;
      };
      }


      static_vector.h



      #pragma once

      #include <array>
      #include <stdexcept>

      #include "aligned_storage_array.h"

      namespace nonstd
      {
      template<class T, std::size_t N>
      struct static_vector
      {
      public:
      using value_type = T;
      using pointer = T*;
      using const_pointer = const T*;
      using reference = value_type&;
      using const_reference = const value_type&;
      using iterator = value_type*;
      using const_iterator = const value_type*;
      using size_type = std::size_t;

      static_vector() = default;
      ~static_vector() { destroy_n(m_size); }

      static_vector(static_vector&& rhs) = default;
      static_vector& operator=(static_vector&& rhs) = default;
      static_vector(const static_vector& rhs) = default;
      static_vector& operator=(const static_vector& rhs) = default;

      // Size and capacity
      constexpr std::size_t size() const { return m_size; }
      constexpr std::size_t max_size() const { return N; }
      constexpr bool empty() const { return m_size == 0; }

      // Iterators
      inline iterator begin() { return &m_data[0]; }
      inline const_iterator begin() const { return &m_data[0]; }
      inline iterator end() { return &m_data[m_size]; }
      inline const_iterator end() const { return &m_data[m_size]; }

      // Access
      inline T& operator(std::size_t pos)
      {
      return m_data[pos];
      }

      inline const T& operator(std::size_t pos) const
      {
      return m_data[pos];
      }

      inline T& at(std::size_t pos)
      {
      if (pos >= m_size)
      throw std::out_of_range("static_vector subscript out of range");
      return m_data.at(pos);
      }

      inline const T& at(std::size_t pos) const
      {
      if (pos >= m_size)
      throw std::out_of_range("static_vector subscript out of range");
      return m_data.at(pos);
      }

      // Operations
      template<typename ...Args>
      inline T& emplace_back(Args&&... args)
      {
      T& result = m_data.bounded_emplace(m_size, args...);
      ++m_size;
      return result;
      }

      inline void clear()
      {
      std::size_t count = m_size;
      m_size = 0; // In case of exception
      destroy_n(count);
      }

      private:
      void destroy_n(std::size_t count)
      {
      for (std::size_t pos = 0; pos < count; ++pos)
      m_data.destroy(pos);
      }

      aligned_storage_array<T, N> m_data;
      std::size_t m_size = 0;
      };
      }


      A full functional test setup is available here (wandbox). The concept lines can be uncommented and will parse in a compiler that supports them. Mostly I'd like some extra eyes to help determine:




      1. Is this actually safe for placement new with respect to alignment?

      2. Is the use of std::launder correct?

      3. Is the use of reinterpret_cast correct (or should it be two static_casts instead?)

      4. Are there any hidden pitfalls I should watch out for here (aside from the maximum capacity)?

      5. Am I being sufficiently paranoid (::new, std::address_of, std::destroy_at)? Any other safety features I can put in to handle risky operator overloads?

      6. How should I actually handle copy and move? The aligned_storage will happily copy irrespective of whether or not T actually has copy and move functions. I don't think these concepts are enough because I'm not calling the actual constructors or operators. However in the array base I don't know which entries are and aren't valid to copy/move.


      I'm told this is similar to something like boost::small_vector, but I want the aligned_storage_array generalized because I want to use it for a number of different static structures later. Also I would like to learn more about alignment/placement new, forwarding, and launder.



      Thank you!









      share







      New contributor




      rtek is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.












      I'm trying to expand on the implementation of static_vector on the std::aligned_storage reference page, but would like to split it into two parts. First, an aligned_storage_array that supports perfect forwarding (so that I can emplace into it) and doesn't require a default constructor, and then the actual static_vector infrastructure that builds upon it. This will let me use the aligned_storage_array for some other static data structures I plan to make in the future.



      aligned_storage_array.h



      #pragma once

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

      namespace nonstd
      {
      template<class T, std::size_t N>
      struct aligned_storage_array
      {
      public:
      aligned_storage_array() = default;
      ~aligned_storage_array() = default;

      aligned_storage_array(aligned_storage_array&& rhs)
      //requires std::is_move_constructible_v<T>
      = default;

      aligned_storage_array& operator=(aligned_storage_array&& rhs)
      //requires std::is_move_assignable_v<T>
      = default;

      aligned_storage_array(const aligned_storage_array& rhs)
      //requires std::is_copy_constructible_v<T>
      = default;

      aligned_storage_array& operator=(const aligned_storage_array& rhs)
      //requires std::is_copy_assignable_v<T>
      = default;

      // Size
      constexpr std::size_t size() const noexcept { return N; }
      constexpr std::size_t max_size() const noexcept { return N; }

      // Access
      inline T& operator(std::size_t pos)
      {
      return *std::launder(
      reinterpret_cast<T*>(
      std::addressof(m_data[pos])));
      }

      inline const T& operator(std::size_t pos) const
      {
      return *std::launder(
      reinterpret_cast<const T*>(
      std::addressof(m_data[pos])));
      }

      inline T& at(std::size_t pos)
      {
      return *std::launder(
      reinterpret_cast<T*>(
      std::addressof(m_data.at(pos))));
      }

      inline const T& at(std::size_t pos) const
      {
      return *std::launder(
      reinterpret_cast<const T*>(
      std::addressof(m_data.at(pos))));
      }

      // Operations
      template<typename ...Args>
      inline T& emplace(size_t pos, Args&&... args)
      {
      return
      *::new(std::addressof(m_data[pos]))
      T(std::forward<Args>(args)...);
      }

      template<typename ...Args>
      inline T& bounded_emplace(size_t pos, Args&&... args)
      {
      return
      *::new(std::addressof(m_data.at(pos)))
      T(std::forward<Args>(args)...);
      }

      inline void destroy(std::size_t pos)
      {
      std::destroy_at(
      std::launder(
      reinterpret_cast<const T*>(
      std::addressof(m_data[pos]))));
      }

      inline void bounded_destroy(std::size_t pos)
      {
      std::destroy_at(
      std::launder(
      reinterpret_cast<const T*>(
      std::addressof(m_data.at(pos)))));
      }

      private:
      std::array<std::aligned_storage_t<sizeof(T), alignof(T)>, N> m_data;
      };
      }


      static_vector.h



      #pragma once

      #include <array>
      #include <stdexcept>

      #include "aligned_storage_array.h"

      namespace nonstd
      {
      template<class T, std::size_t N>
      struct static_vector
      {
      public:
      using value_type = T;
      using pointer = T*;
      using const_pointer = const T*;
      using reference = value_type&;
      using const_reference = const value_type&;
      using iterator = value_type*;
      using const_iterator = const value_type*;
      using size_type = std::size_t;

      static_vector() = default;
      ~static_vector() { destroy_n(m_size); }

      static_vector(static_vector&& rhs) = default;
      static_vector& operator=(static_vector&& rhs) = default;
      static_vector(const static_vector& rhs) = default;
      static_vector& operator=(const static_vector& rhs) = default;

      // Size and capacity
      constexpr std::size_t size() const { return m_size; }
      constexpr std::size_t max_size() const { return N; }
      constexpr bool empty() const { return m_size == 0; }

      // Iterators
      inline iterator begin() { return &m_data[0]; }
      inline const_iterator begin() const { return &m_data[0]; }
      inline iterator end() { return &m_data[m_size]; }
      inline const_iterator end() const { return &m_data[m_size]; }

      // Access
      inline T& operator(std::size_t pos)
      {
      return m_data[pos];
      }

      inline const T& operator(std::size_t pos) const
      {
      return m_data[pos];
      }

      inline T& at(std::size_t pos)
      {
      if (pos >= m_size)
      throw std::out_of_range("static_vector subscript out of range");
      return m_data.at(pos);
      }

      inline const T& at(std::size_t pos) const
      {
      if (pos >= m_size)
      throw std::out_of_range("static_vector subscript out of range");
      return m_data.at(pos);
      }

      // Operations
      template<typename ...Args>
      inline T& emplace_back(Args&&... args)
      {
      T& result = m_data.bounded_emplace(m_size, args...);
      ++m_size;
      return result;
      }

      inline void clear()
      {
      std::size_t count = m_size;
      m_size = 0; // In case of exception
      destroy_n(count);
      }

      private:
      void destroy_n(std::size_t count)
      {
      for (std::size_t pos = 0; pos < count; ++pos)
      m_data.destroy(pos);
      }

      aligned_storage_array<T, N> m_data;
      std::size_t m_size = 0;
      };
      }


      A full functional test setup is available here (wandbox). The concept lines can be uncommented and will parse in a compiler that supports them. Mostly I'd like some extra eyes to help determine:




      1. Is this actually safe for placement new with respect to alignment?

      2. Is the use of std::launder correct?

      3. Is the use of reinterpret_cast correct (or should it be two static_casts instead?)

      4. Are there any hidden pitfalls I should watch out for here (aside from the maximum capacity)?

      5. Am I being sufficiently paranoid (::new, std::address_of, std::destroy_at)? Any other safety features I can put in to handle risky operator overloads?

      6. How should I actually handle copy and move? The aligned_storage will happily copy irrespective of whether or not T actually has copy and move functions. I don't think these concepts are enough because I'm not calling the actual constructors or operators. However in the array base I don't know which entries are and aren't valid to copy/move.


      I'm told this is similar to something like boost::small_vector, but I want the aligned_storage_array generalized because I want to use it for a number of different static structures later. Also I would like to learn more about alignment/placement new, forwarding, and launder.



      Thank you!







      c++ memory-management template c++17 sfinae





      share







      New contributor




      rtek is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.










      share







      New contributor




      rtek is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.








      share



      share






      New contributor




      rtek is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.









      asked 9 mins ago









      rtekrtek

      61




      61




      New contributor




      rtek is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.





      New contributor





      rtek is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.






      rtek is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
      Check out our Code of Conduct.






















          0






          active

          oldest

          votes











          Your Answer





          StackExchange.ifUsing("editor", function () {
          return StackExchange.using("mathjaxEditing", function () {
          StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
          StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
          });
          });
          }, "mathjax-editing");

          StackExchange.ifUsing("editor", function () {
          StackExchange.using("externalEditor", function () {
          StackExchange.using("snippets", function () {
          StackExchange.snippets.init();
          });
          });
          }, "code-snippets");

          StackExchange.ready(function() {
          var channelOptions = {
          tags: "".split(" "),
          id: "196"
          };
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function() {
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled) {
          StackExchange.using("snippets", function() {
          createEditor();
          });
          }
          else {
          createEditor();
          }
          });

          function createEditor() {
          StackExchange.prepareEditor({
          heartbeatType: 'answer',
          autoActivateHeartbeat: false,
          convertImagesToLinks: false,
          noModals: true,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: null,
          bindNavPrevention: true,
          postfix: "",
          imageUploader: {
          brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
          contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
          allowUrls: true
          },
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          });


          }
          });






          rtek is a new contributor. Be nice, and check out our Code of Conduct.










          draft saved

          draft discarded


















          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f211362%2fimplementation-of-static-vector-using-an-array-of-stdaligned-storage-with-std%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown

























          0






          active

          oldest

          votes








          0






          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes








          rtek is a new contributor. Be nice, and check out our Code of Conduct.










          draft saved

          draft discarded


















          rtek is a new contributor. Be nice, and check out our Code of Conduct.













          rtek is a new contributor. Be nice, and check out our Code of Conduct.












          rtek is a new contributor. Be nice, and check out our Code of Conduct.
















          Thanks for contributing an answer to Code Review Stack Exchange!


          • Please be sure to answer the question. Provide details and share your research!

          But avoid



          • Asking for help, clarification, or responding to other answers.

          • Making statements based on opinion; back them up with references or personal experience.


          Use MathJax to format equations. MathJax reference.


          To learn more, see our tips on writing great answers.




          draft saved


          draft discarded














          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f211362%2fimplementation-of-static-vector-using-an-array-of-stdaligned-storage-with-std%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown





















































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown

































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown







          Popular posts from this blog

          404 Error Contact Form 7 ajax form submitting

          How to know if a Active Directory user can login interactively

          Refactoring coordinates for Minecraft Pi buildings written in Python