Возвращает кортеж, содержащий C-массив

#c #c 20 #stdtuple

#c #c 20 #стандартный кортеж

Вопрос:

У меня есть некоторый код, который компилируется и работает нормально под g 10.2.0, но отклоняется clang 11.0.0.

Вот минимальное средство воспроизведения проблемы:

 #include <tuple>
#include <cstdint>

struct Dummy { };

using second_t = Dummy;
using example_t = std::tuple<size_t, second_t[8]>;

example_t f() {
    example_t resu<
    return resu<
}

int main() {
    auto x = f();
    (void) x;
}
 

В g он компилируется без жалоб, но с помощью clang я получаю:

 clang   --std=c  20 -Wall -Werror main.cpp -o example
In file included from main.cpp:1:
/usr/bin/../lib/gcc/aarch64-unknown-linux-gnu/10.2.0/../../../../include/c  /10.2.0/tuple:137:4: error: array initializer must be an initializer list
        : _M_head_impl(std::forward<_UHead>(__h)) { }
          ^
/usr/bin/../lib/gcc/aarch64-unknown-linux-gnu/10.2.0/../../../../include/c  /10.2.0/tuple:375:9: note: in instantiation of function template specialization 'std::_Head_base<1, Dummy [8], false>::_Head_base<Dummy [8]>' requested here
      : _Base(std::forward<_Head>(_M_head(__in))) { }
        ^
/usr/bin/../lib/gcc/aarch64-unknown-linux-gnu/10.2.0/../../../../include/c  /10.2.0/tuple:236:9: note: in instantiation of member function 'std::_Tuple_impl<1, Dummy [8]>::_Tuple_impl' requested here
      : _Inherited(std::move(_M_tail(__in))),
        ^
/usr/bin/../lib/gcc/aarch64-unknown-linux-gnu/10.2.0/../../../../include/c  /10.2.0/tuple:996:17: note: in instantiation of member function 'std::_Tuple_impl<0, unsigned long, Dummy [8]>::_Tuple_impl' requested here
      constexpr tuple(tupleamp;amp;) = default;
                ^
main.cpp:11:12: note: in defaulted move constructor for 'std::tuple<unsigned long, Dummy [8]>' first required here
    return resu<
           ^
1 error generated.
make: *** [Makefile:2: all] Error 1
 

Интересно, что если я изменю second_t значение на an int32_t , я тоже получу сообщение об ошибке от g :

 g   --std=c  20 -Wall -Werror main.cpp -o example
In file included from main.cpp:1:
/usr/include/c  /10.2.0/tuple: In instantiation of ‘constexpr std::_Head_base<_Idx, _Head, false>::_Head_base(_UHeadamp;amp;) [with _UHead = int [8]; long unsigned int _Idx = 1; _Head = int [8]]’:
/usr/include/c  /10.2.0/tuple:375:49:   required from ‘constexpr std::_Tuple_impl<_Idx, _Head>::_Tuple_impl(std::_Tuple_impl<_Idx, _Head>amp;amp;) [with long unsigned int _Idx = 1; _Head = int [8]]’
/usr/include/c  /10.2.0/tuple:237:42:   required from ‘constexpr std::_Tuple_impl<_Idx, _Head, _Tail ...>::_Tuple_impl(std::_Tuple_impl<_Idx, _Head, _Tail ...>amp;amp;) [with long unsigned int _Idx = 0; _Head = long unsigned int; _Tail = {int [8]}]’
/usr/include/c  /10.2.0/tuple:996:17:   required from here
/usr/include/c  /10.2.0/tuple:137:42: error: array used as initializer
  137 |  : _M_head_impl(std::forward<_UHead>(__h)) { }
      |                                          ^
make: *** [Makefile:2: all] Error 1
 

Я предполагаю, что это связано с использованием C-массива. Если я изменю код для использования std::array вместо этого, проблема исчезнет. Тем не менее, мне все еще интересно, есть ли способ сделать это с C-array ради любопытства.

Итак, мои вопросы:

  1. Был ли g неправ, принимая этот код?
  2. Есть ли какой-либо способ вернуть C-массив внутри такого кортежа, который работает для g и clang ?

Комментарии:

1. Кошерно ли, согласно стандарту C , иметь простой массив в кортеже — это прекрасный вопрос. Однако, каким бы ни был ответ: это всего лишь одна из тех вещей, которые нецелесообразны, и даже если это правильно, я бы этого не сделал. Если вам нужен массив в кортеже, используйте std::array вместо него обычный массив. Вы не заметите другого, и будет меньше неожиданных сюрпризов, с которыми придется иметь дело, и меньше головной боли.

2. @SamVarshavchik хотя я согласен, я не могу этого сделать, согласно моему комментарию относительно std::array в вопросе 🙂

3. std::array буквально template <typename T, std::size_t N> struct array { functions; private: T data[N]; }; . Если массив безопасен для вас, то и std::array

4. я думаю, это потому, что для конструктора копирования / перемещения кортежа : std::is_move_constructible<Ti>::value must be true for all i, otherwise the behavior is undefined . Для C-массивов это неверно, поэтому мы получаем неопределенное поведение

5. Если вам не нравится std::array , вы всегда можете использовать struct scared_of_the_STL { second_t data[8]; }; . Это распространено в C, чтобы иметь легко копируемые массивы.

Ответ №1:

Массивы C — это в основном указатели, и обратите внимание, что они не предоставляют и не нуждаются в информации о длине массива. длина должна храниться отдельно и передаваться по кругу. Поэтому одним из способов реализации того, что вам нужно, было бы:

   using example_t = std::tuple<size_t, second_t*>;