2016-04-12 4 views
4

Скажем, у меня есть этот класс:Как заполнить массив константных элементов на основе аргумента конструктора?

template<int N> 
class A { 
    public: 
    A(const char *s) ... 
    private: 
    const char buf[N]; 
}; 

шаблона есть, так что я могу настроить размер массива без динамического выделения памяти (требование). Элемент buf равен const, поскольку он должен оставаться постоянным в течение всего срока службы объекта после инициализации объекта.

Чтобы уточнить, у меня также нет доступа к STL.

Каковы мои возможности для определения этого конструктора, чтобы я мог скопировать содержимое s в buf? Один из вариантов - const_cast, но я ищу альтернативы, которые этого не требуют.

+2

Можете ли вы изменить 'char buf [N]' на 'std :: array'? – Jarod42

+0

@ Jarod42 Нет, к сожалению. У меня нет доступа к STL. – Ana

+0

@ Вы можете скопировать источники libstdC++ или что-то еще? Даже если у вас нет доступа к 'std :: array', вы переделаете его прямо сейчас. Наверное, хуже, чем в оригинальной реализации. –

ответ

3

Вы используете расширение index_sequence и шаблона.

#include <utility> 
#include <cstdlib> 


template<int N> 
class A { 

    template<size_t...Is> 
    A(const char (&s)[N], std::index_sequence<Is...>) 
    : buf{ s[Is]... } 
    {} 


    public: 
    A(const char (&s)[N]) 
     : A(s, std::make_index_sequence<N>()) 
    { 
    } 

    private: 
    const char buf[N]; 
}; 


int main() 
{ 
    A<3> a("ab"); 

}; 

И потому, что Const символ [] является буквальным типа, он также позволяет классу быть constexpr:

#include <utility> 
#include <cstdlib> 


template<int N> 
class A { 

    template<size_t...Is> 
    constexpr A(const char (&s)[N], std::index_sequence<Is...>) 
    : buf{ s[Is]... } 
    {} 


    public: 
    constexpr A(const char (&s)[N]) 
     : A(s, std::make_index_sequence<N>()) 
    { 
    } 

    private: 
    const char buf[N]; 
}; 


int main() 
{ 
    constexpr A<3> a("ab"); 

}; 

но изменяет подпись ...

ОК, то это:

#include <utility> 
#include <cstdlib> 
#include <cstring> 


template<int N> 
class A { 

    template<size_t...Is> 
    constexpr A(const char *s, size_t len, std::index_sequence<Is...>) 
    : buf{ (Is <= len ? s[Is] : char(0))... } 
    {} 


    public: 
    constexpr A(const char *s) 
     : A(s, strlen(s), std::make_index_sequence<N>()) 
    { 
    } 

    private: 
    const char buf[N]; 
}; 


int main() 
{ 
    constexpr A<10> a("ab"); 

}; 
+2

Обратите внимание, что вы меняете подпись, чтобы точно «N» char. – Jarod42

+0

@ Jarod42 мы можем решить это ... sec –

+0

@RichardHodges: Я решил, что: P (см. Мое решение) – Nawaz

4

Решение, предоставленное @Richard Hodges, требует, чтобы класс был инициализирован массивом символов, а не char const*, который изменяет подпись конструктора. Если это не сработает для вашего случая, то здесь одно решение, которое делает не изменить подпись:

template<int N> 
class A 
{ 
     template<size_t...Is> 
     A(const char * s, std::index_sequence<Is...>) 
     : _assert(std::strlen(s) <= N, "size of buffer is bigger than N"), 
      buf{ (Is, *s != '\0'? *s++: *s)... } 
     {} 

    public: 
     A(const char *s) : A(s, std::make_index_sequence<N>()) {} 

    private: 
     throwable _assert; 
     const char buf[N]; 
}; 

где throwable определяется как:

struct throwable 
{ 
    throwable(bool b, char const * message){ 
     if (not b) 
      throw std::invalid_argument(message); 
    } 
}; 

Использование throwable гарантирует, что buf не инициализируется буфером, превышающим N байтов. Если, однако, ваша ситуация гарантирует, что и, следовательно, эта проверка не нужна, вы можете ее удалить. Код должен работать и без него, хотя я бы предложил вам сохранить его как минимум в debug.

Обратите внимание, что добавление _assert в качестве члена увеличивает размер класса как минимум на один байт. Чтобы избежать этого, мы могли бы использовать empty base class optimiation и сделать это вместо того, чтобы:

template<int N> 
class A : private throwable 
{ 
     template<size_t...Is> 
     A(const char * s, std::index_sequence<Is...>) 
     : throwable(std::strlen(s) <= N, "size of buffer is bigger than N"), 
      buf{ (Is, *s != '\0'? *s++: *s)... } 
     {} 
    public: 
     A(const char *s) : A(s, std::make_index_sequence<N>()) {} 

    private: 
     const char buf[N]; 
}; 

Это экономит нам 1 байт для каждого экземпляра.

+0

примечание: требуется C++ 14 –

+0

@ M.M: эквиваленты 'std :: make_index_sequence' и' std :: index_sequence' могут быть реализованы и в C++ 11. – Nawaz

+1

Мне нравится throwable, если вы даете ему конструктор constexpr (и то же самое с A), тогда вы можете объявить переменную constexpr - и это даст вам исключение для компиляции с трассировкой стека, если длина строки неверна. –