Список инициализаторов передается как параметр функции для массива

Как мне заставить это работать:

void foo(uint8_t a[]) { ... }

foo({0x01, 0x02, 0x03});

Это дает мне ошибку:

error: cannot convert '<brace-enclosed initializer list>' to 'uint8_t* {aka unsigned char*}' for argument '1'
                                                     ^

person Timmmm    schedule 21.07.2015    source источник
comment
Проблема была бы яснее, если бы вы избежали запутанного типа параметра uint8_t[] и вместо него написали более точный uint8_t*. :)   -  person Lightness Races in Orbit    schedule 21.07.2015
comment
Извините, да, я обычно использовал форму указателя, но мне было интересно, может ли компилятор выполнить какую-то магию, о которой я не знал, если бы вместо этого использовал синтаксис массива.   -  person Timmmm    schedule 21.07.2015
comment
Нет, 100% эквивалентность.   -  person Lightness Races in Orbit    schedule 22.07.2015


Ответы (5)


Ответы до сих пор не касались основной проблемы с вопросом: в подписи

void foo(uint8_t a[])

a — это не массив, а указатель на uint8_t. И это несмотря на то, что объявление a делает его похожим на массив. На это указывает даже сообщение об ошибке:

cannot convert '<brace-enclosed initializer list>' to 'uint8_t* {aka unsigned char*}'

Итак, точно так же вам не разрешено делать это:

uint8_t *a = {0x01, 0x02, 0x03}; // Eek! Error

Вы не можете позвонить foo({0x01, 0x02, 0x03}); С подписью выше.

Я предлагаю вам потратить некоторое время, чтобы прочитать о массивах в стиле C и о том, как они не первоклассные граждане в C++.

Из ответа, который вы опубликовали на свой собственный вопрос, кажется, что вы ищете функцию, которая работает для массивов фиксированного размера. Но не передавайте его по значению! Я рекомендую использовать следующее объявление:

void foo(std::array<uint8_t, 3> const &a);
person nasser-sh    schedule 21.07.2015

Это сработало для меня, мне пришлось изменить сигнатуру функции, но на самом деле это лучше в моем случае, поскольку она статически проверяет длину массива:

void foo(std::array<uint8_t, 3> a) { /* use a.data() instead of a */ }

foo({0x01, 0x02, 0x03}); // OK

foo({0x01, 0x02}); // Works, at least on GCC 4.9.1. The third value is set to zero.

foo({0x01, 0x02, 0x03, 0x04}); // Compilation error.
person Timmmm    schedule 21.07.2015
comment
Массивы в стиле C отстойны во всем. Всегда используйте std::array поверх них, если это возможно. - person Puppy; 21.07.2015
comment
Возможно, вы можете использовать такой шаблон, как template<int size> void foo(std::array<uint8_t, size> a){/*...*/}... (я еще не пробовал так раньше..) - person Shindou; 21.07.2015
comment
@Shindou не в этом случае, параметр size не может быть выведен. - person ForEveR; 21.07.2015
comment
@ForEveR Я только что проверил. мы должны назвать это как foo<size>({/*...*/});, чтобы заставить его работать - person Shindou; 21.07.2015
comment
@Shindou: Это то, что он только что сказал - person Lightness Races in Orbit; 21.07.2015
comment
Так что это странно... теперь, когда я пытаюсь это сделать, я не получаю ошибку компиляции для массива неправильного размера. Может ли кто-нибудь подтвердить, делают они это или нет, и нужно ли вам. Может быть ошибка GCC (я использую 4.9.1). - person Timmmm; 10.09.2015
comment
На самом деле после тестирования кажется, что он позволяет использовать более короткие списки инициализаторов и заполняет их нулями, но более длинные вызывают ошибку. Возможно, я солгал в своем первоначальном ответе и фактически проверил его с более длинным списком. - person Timmmm; 10.09.2015

Вы не можете. Просто построить

uint8_t a[] = {0x01, 0x02, 0x03};

и позвоните foo(a).

Или просто используйте std::array, это, вероятно, лучше.

person ForEveR    schedule 21.07.2015

Этот:

void foo(uint8_t a[]) { ... }

это функция, которая принимает uint8_t*, а не массив - массивы распадаются на указатели при использовании в качестве аргументов функции. Проблема в том, что список инициализаторов (например, {0x01, 0x02, 0x03}) нельзя преобразовать в uint8_t*.

Если вы хотите передать произвольное число uint8_ts в foo, простое решение — использовать новый std::initializer_list

void foo(std::initializer_list<uint8_t> a) { ... }

foo({0x01, 0x02, 0x03, 0x04, 0x05}); // OK - a has 5 elems in it

Или вы можете взять вариативный пакет и построить из него массив внутри:

template <typename... Args,
          typename = std::enable_if_t<
              all_true<std::is_convertible<Args, uint8_t>::value...>
              >>
void foo(Args... elems) {
    uint8_t a[] = {elems...};
    // ...
}

Это имеет немного другое использование:

foo({0x01, 0x02, 0x03}); // error
foo(0x01, 0x02, 0x03; // OK - a has 3 elems in it
person Barry    schedule 21.07.2015

person    schedule
comment
Массив std::array создается перед функцией и существует до тех пор, пока функция не вернется. - person Bruno Martinez; 01.03.2017