списки символов и инициализаторов

Я хотел бы передать некоторые числовые значения байтов через список инициализаторов вариативный шаблон в массив. Это возможно?

template < int N > struct a {
  char s[N];
  template < typename ... A >
  a (A ... _a) : s {_a...} {}
};

int main () {
  // g++-4.5: error: narrowing conversion of »_a#0« from »int« to »char« inside { }
  a < 3 > x { 1, 2, 3 };
}

Что я могу придумать, так это

  • использовать восьмеричное представление, '\001' и т. д., или
  • для приведения каждого значения.

Но и то, и другое не устраивает.


person Thomas    schedule 15.09.2010    source источник
comment
Почему возникает проблема приведения значений? Если вы хотите быть осторожным с типом приведения, используйте boost::numeric_cast и специально разрешите сужение конверсий. Так или иначе, вы сужаете эти аргументы.   -  person Potatoswatter    schedule 16.09.2010
comment
Если вы не предоставите никакого конструктора, вы сможете использовать инициализацию фигурных скобок для агрегатов.   -  person sellibitze    schedule 16.09.2010


Ответы (3)


ПРИМЕЧАНИЕ. Во всем этом нет необходимости, если только вы не добавили в класс функциональные возможности, чтобы он больше не был агрегатом. (Например, другие конструкторы, закрытые члены, базовый класс и т. д.). Непосредственный способ исправить код в вопросе — просто удалить конструктор. Итак, давайте предположим, что в этом есть что-то еще.

Я видел, как некоторые люди пытались делать что-то подобное. Это кажется уродливым, иметь дело с семантикой преобразования и пытаться искусственно воссоздать функциональность обычного вызова функции.

Вот стратегия создания класса массива, у которого в первую очередь просто есть правильный конструктор.

Псевдонимы шаблонов добавили бы вишенки на торт, скрыв ::type уродство, но этого пока нет в GCC.

template< typename ... NT >
struct var_ctor_array {
    enum { size_e = 0 }; // only used for zero size case
};

template< typename T, typename ... NT >
struct var_ctor_array< T, NT ... > {
    enum { size_e = 1 + sizeof...( NT ) };

    T st[ size_e ];

    var_ctor_array( T elem0, NT ... elemN )
        : st { elem0, elemN ... } {}
};

template< typename T, size_t N, typename ... NT >
struct gen_var_ctor_array {
    typedef typename gen_var_ctor_array< T, N-1, T, NT ... >::type type;
};

template< typename T, typename ... NT >
struct gen_var_ctor_array< T, 0, NT ... > {
    typedef var_ctor_array< NT ... > type;
};

int main() { // usage
    gen_var_ctor_array< char, 5 >::type five( 1, 2, 3, 4, 5 );
}
person Potatoswatter    schedule 16.09.2010
comment
Отличная идея, я буду использовать классы. g++ не будет компилироваться без особого случая для size_e == 0 из-за ошибки. Теперь можно ли использовать T() в качестве значения по умолчанию для каждого параметра? - person Thomas; 17.09.2010
comment
@Thomas: попробуйте, пакет параметров «elemN» не может иметь аргумент по умолчанию. Однако эту функцию можно взломать с помощью наследующих конструкторов, которых нет в GCC 4.5. - person Potatoswatter; 17.09.2010
comment
Что вызывает этот баг? gen_var_ctor_array< char, 0 >::type zero; работает… - person Potatoswatter; 17.09.2010
comment
Я имел в виду: удаление первых четырех строк (потому что у меня не будет массивов размера 0) и удаление списка шаблонов в строке 7 приведет к сбою g++. (Но, конечно, в любом случае добавление специального случая лучше). - person Thomas; 17.09.2010
comment
@Thomas: Ах, да, это неотъемлемая часть. Используемый только комментарий относится только к одной строке внутри фигурных скобок. - person Potatoswatter; 17.09.2010

Вам не нужен сложный код

template < int N > struct a {
  char s[N];
  template < typename ... A >
  a (A ... _a) : s {static_cast<char>(_a)...} {}
};
person Johannes Schaub - litb    schedule 14.01.2011

На самом деле вы не используете списки инициализаторов. Конструктор получает вариативный шаблон, и вы инициализируете x с помощью универсальная инициализация.

Единственная проблема в том, что я не знаю элегантного способа инициализации массива с помощью initializer_list, AFAIK std::array должен иметь конструктор, который принимает initializer_list, но, похоже, он еще не поддерживается g++.

#include <utility>
template < int N > struct a {
    char s[N];

    a (std::initializer_list<char> list) {
        if (N != list.size()) 
            throw "list wrong size";

        int i = 0;
        const char* p = list.begin();
        while(p != list.end())
            s[i++] = *p++;
    }
};
person Motti    schedule 15.09.2010
comment
Вы абсолютно правы, вариативные шаблоны — это то, что я хотел написать. Списки инициализаторов, возможно, не так эффективны для инициализации массива. - person Thomas; 16.09.2010
comment
array не нужно поддерживать initializer_list, потому что он уже делает то же самое в C++03, будучи агрегатом. Нет ничего более эффективного, чем это, так как это равносильно инициализации членов на месте. - person Potatoswatter; 16.09.2010