Size() std:: указатель массива в контексте constexpr
Скажем, у меня есть функция вроде:
int test(std::array<char, 8>* data) {
char buffer[data->size() * 2];
[... some code ...]
}
ясно, размер буфера может быть оценен во время компиляции: данные имеют размер constexpr
из 8 элементов, 8 * 2 = 16 байт.
Однако при компиляции с -Wall
, -pedantic
и -std=c++11
я получаю печально известную ошибку:
предупреждение: массивы переменной длины - это функция C99 [-Wvla-extension]
который, как мне кажется, имеет смысл: array::size()
is constexpr
, но он по-прежнему является методом, и в приведенной выше функции нам еще нужно разыменовать указатель, который не является constexpr
.
Если я попробую что-то вроде:
int test(std::array<char, 8>& data) {
char buffer[data.size() * 2];
[...]
}
gcc
(проверенная версия 5.2.0) кажется счастливой: предупреждения нет.
Но с clang++
(3.5.1) я все еще получаю предупреждение, жалующееся на массивы переменной длины.
В моем случае я не могу легко изменить подпись test
, он должен взять указатель. Итак... несколько вопросов:
-
Каков наилучший/самый стандартный способ получить размер указателя std::array
в контексте constexpr?
-
Является ли разница в поведении с указателями против ожидаемых ссылок? Какой компилятор прав относительно предупреждения, gcc
или clang
?
Ответы
Ответ 1
Я не знаю о 2.
Но для 1 мы можем сделать это:
template<class T, size_t N>
constexpr std::integral_constant<size_t, N> array_size( std::array<T, N> const& ) {
return {};
}
то
void test(std::array<char, 8>* data) {
using size=decltype(array_size(*data));
char buffer[size{}];
(void)buffer;
// [... some code ...]
}
в качестве альтернативы:
template<class T, class U, size_t N>
std::array<T,N> same_sized_array( std::array< U, N > const& ) {
return {};
}
void test(std::array<char, 8>* data) {
auto buffer = same_sized_array<char>(*data);
(void)buffer;
// [... some code ...]
}
наконец, очистка С++ 14:
template<class A>
constexpr const decltype(array_size( std::declval<A>() )) array_size_v = {};
void test3(std::array<char, 8>* data) {
char buffer[array_size_v<decltype(*data)>];
(void)buffer;
// [... some code ...]
}
Живой пример.
Ответ 2
Хорошим старым способом C было бы определение, но С++ имеет const int
или для С++ 11 constexpr
. Поэтому, если вы хотите, чтобы компилятор знал, что размер массива является постоянной времени компиляции, самым портативным (*) способом было бы сделать его const
или constexpr
:
#include <iostream>
#include <array>
const size_t sz = 8; // constexpr size_t sz for c++11
int test(std::array<char, sz>* data) {
char buffer[sz * 2];
buffer[0] = 0;
return 0;
}
int main()
{
std::array<char, sz> arr = { { 48,49,50,51,52,53,54,55 } };
int cr = test(&arr);
std::cout << cr << std::endl;
return 0;
}
Он компилируется без предупреждения даже при -Wall -pedantic
под Clang 3.4.1
Во втором вопросе я не могу представить, почему gcc делает эту разницу между указателями и ссылками здесь. Либо он может определить, что метод size()
на std::array
, размер которого является константой, является константным выражением - и он должен позволять как - либо не может - и он должен вызывать одно и то же предупреждение для обоих. Но это касается не только компилятора, но и стандартной реализации библиотеки.
Реальная проблема заключается в том, что pre-С++ 11 std:: array не был частью стандартной библиотеки, а constexpr
также определен только с С++ 11 on. Поэтому в режиме pre-С++ 11 оба процесса компилятора std:: array в качестве расширения, но метод size
не может объявить его возвращаемое значение константой expr. Это объясняет, почему Clang (и gcc обращается к указателю) выдает предупреждение.
Но если вы скомпилируете исходный код в режиме С++ 11 (-std=c++11
), у вас не должно быть предупреждения, потому что для стандартного метода size()
для std::array
должен быть constexpr
.
(*) Вопрос о лучшем/наиболее стандартном; Я не могу сказать, что лучше всего, и я не могу определить большинство стандартных, поэтому я придерживаюсь того, что я буду использовать, если бы хотел избежать проблем с переносимостью на компиляторах, отличных от С++ 11.
Ответ 3
Как насчет использования std::tuple_size
в объявлении вашего параметра?
void test(std::array<char, 8>* data) {
using data_type = std::remove_pointer<decltype(data)>::type;
char buffer[std::tuple_size<data_type>::value * 2];
static_assert(sizeof buffer == 16, "Ouch");
// [... some code ...]
}