After reading this article, I've decided to take a crack at implementing a safe_strcpy function that would remove some of the errors that the author speaks about. Obviously, my safe_strcpy() function is inspired by his. Below, I've created a simple class that houses a std::array of one of the many different kinds of strings. My safe_strcpy() functions rely on always null-terminating the array given.I have a few worries about my implementation:The lines with out[N - 1] = static_cast<T>(0) seem wrong when the underlying character type is wchar_t. In this sense, I want the array to terminate with something to the effect of L0. Will this work? If not, how can I fix it?Should I be concerned with any performance issues or any gotchas that I didn't think of?Is this a viable solution to the safe_strcpy conundrum? In other words, would you use this class in production code? What kind of improvements or extensions should this class use?#include <cstdlib>#include <cstdio>#include <cstring>#include <array>#include <iostream>#include <typeinfo>using namespace std;template <size_t N, class T = char>struct char_array{    typedef T                value_type;    typedef const T          const_value_type;    typedef T&               reference;    typedef const T&         const_reference;    typedef std::array<T, N> container;    typedef typename container::size_type       size_type;    typedef typename container::iterator        iterator;    typedef typename container::const_iterator  const_iterator;    typedef typename container::reverse_iterator        reverse_iterator;    typedef typename container::const_reverse_iterator  const_reverse_iterator;    /* Constructor */    char_array()    {        static_assert((is_same<unsigned char, T>::value || is_same<char, T>::value ||                      is_same<wchar_t, T>::value || is_same<signed char, T>::value ||                      is_same<char16_t, T>::value || is_same<char32_t, T>::value)  &&                      N > 0,                      char_array initialized with invalid type or size);    }    /* Destructor */    ~char_array() {}    /* Copy constructor and assignment */    char_array(const char_array<N,T> &other) = default;    char_array& operator=(const char_array<N,T> &other) = default;    /* Move constructor and assignment */    char_array(char_array<N,T> &&other) { buf_ = std::move(other.buf_); }    char_array& operator=(char_array<N,T> &&other)     {         buf_ = std::move(other.buf_);        return *this;    }     /* Delegate members of internal container */    iterator         begin() { return buf_.begin(); }    const_iterator   begin() const { return buf_.begin(); }    iterator         end() { return buf_.end(); }    const_iterator   end() const { return buf_.end(); }    const_iterator   cbegin() const { return buf_.cbegin(); }        const_iterator   cend() const { return buf_.cend(); }    reverse_iterator         rbegin() { return buf_.rbegin(); }    const_reverse_iterator   rbegin() const { return buf_.rbegin(); }    reverse_iterator         rend() { return buf_.rend(); }    const_reverse_iterator   rend() const { return buf_.rend(); }    const_reverse_iterator   crbegin() const { return buf_.crbegin(); }    const_reverse_iterator   crend() const { return buf_.crend(); }    reference       operator[](size_type i) { return buf_[i]; }    const_reference operator[](size_type i) const { return buf_[i]; }    size_type        size() const { return buf_.size(); }    size_type        max_size() const { return buf_.max_size(); }    bool             empty() const { return buf_.empty(); }    reference        at(size_type n) { return buf_.at(n); }    const_reference  at(size_type n) const { return buf_.at(n); }    reference        front(size_type n) { return buf_.front(n); }    const_reference  front(size_type n) const { return buf_.front(n); }    reference        back(size_type n) { return buf_.back(n); }    const_reference  back(size_type n) const { return buf_.back(n); }    value_type*       data() { return buf_.data(); }    const value_type* data() const { return buf_.data(); }    void fill(const value_type &val) { buf_.fill(val); }    void swap(char_array &x) { buf_.swap(x.buf_); }private:    container buf_;};/*    Allow template magic to automatically deduce    the size of the given array. NOTE: this only works with    statically-allocated arrays (e.g. arrays with a size known    at compile time)    @param[in] - out - a reference to the array    @param[in] - src - the source string    Usage:    char buf[10];    safe_strcpy(buf, This is a string > 10 chars); */template <class T, size_t N>void safe_strcpy(T (&out)[N], const T *src){    static_assert((is_same<wchar_t, T>::value || is_same<signed char, T>::value ||                is_same<unsigned char, T>::value || is_same<char, T>::value ||                 is_same<char16_t, T>::value || is_same<char32_t, T>::value)  &&                N > 0, Incompatible type for safe_strcpy);    memcpy(out, src, N * sizeof(T));    out[N - 1] = static_cast<T>(0);}template <class T, size_t N, class S>void safe_strcpy(char_array<N,T> &out, const S *src){    static_assert(is_same<T, S>::value, Source content type different from char_array destination);    memcpy(out.data(), src, N * sizeof(T));    out[N - 1] = static_cast<T>(0);}template <class Os, size_t N, class T>Os& operator<<(Os &os, const char_array<N,T> &arr){    for (const auto &c : arr) os << c;    return os;}int main(){    wchar_t buf[10];    safe_strcpy(buf, Lreally long string that's greater than 10 freaking characters!!!!);    wcout << buf << \n;    char_array<10> buffer;    safe_strcpy(buffer, This is a string longer than 10 chars);    cout << Max size is:  << buffer.max_size() << \n;    cout << buffer << \n;}
Safe strcpy implementation with C++
c++;strings;c++11;c++14