Как убедиться, что в функции constexpr, принимающей массив, массив заканчивается NULL?

Следующий код предназначен для создания своего рода тривиального хэша строки длиной до 8 символов:

#include <type_traits>
#include <cstdint>
#include <iostream>

template<std::size_t N, std::size_t n=N>
constexpr typename std::enable_if<N<=9 && n==0,
uint64_t>::type string_hash(const char (&)[N])
{
    return 0;
}

template<std::size_t N, std::size_t n=N>
constexpr typename std::enable_if<N<=9 && n!=0,
uint64_t>::type string_hash(const char (&array)[N])
{
    return string_hash<N,n-1>(array) | ((array[n-1]&0xffull)<<(8*(n-1)));
}

Для обычных строковых литералов и constexpr строк с нулевым завершением это действительно работает нормально. Но если я сделаю что-то вроде этого:

constexpr char s2[] = {1,2,3,4,5,6,7,8,9};
std::cout << string_hash(s2) << "\n";

, вывод будет таким же, как и для строки "\x1\x2\x3\x4\x5\x6\x7\x8". Я пытался добавить static_assert(array[N-1]==0,"Failed"); в определение string_hash, но компилятор говорит, что array[N-1] не является константным выражением. Затем я попытался объявить параметр constexpr, но компилятор сказал, что параметр нельзя объявить constexpr.

Как я могу тогда сделать эту проверку?


person Ruslan    schedule 01.08.2015    source источник
comment
@ CygnusX1: Это не те входные данные, на которые жалуется ОП. Откуда вы взяли "ugabuga"?   -  person Lightness Races in Orbit    schedule 01.08.2015


Ответы (2)


Имейте в виду, что хотя constexpr функции могут использоваться во время компиляции, это не обязательно. Вы не можете добавлять какие-либо статические утверждения для параметров времени выполнения, потому что статическое утверждение было бы невозможно оценить, если параметры неизвестны во время компиляции.

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

Тело функции constexpr должно быть одним оператором return в C++11, но вы все равно можете поместить его туда:

return array[N-1] ? throw "bad!" : <your current return expression here>;

Выбери что-нибудь получше, чтобы бросить.

person Community    schedule 01.08.2015
comment
Throwing будет печатать ошибку только во время выполнения. Ошибка во время компиляции, вероятно, была бы предпочтительнее. - person CygnusX1; 01.08.2015
comment
@ CygnusX1 Я уже рассказал об этом в своем ответе. Если функция используется в контексте, где требуется постоянное выражение, и передается неверный ввод, то она будет отклонена во время компиляции. Если функция используется в контексте, где константное выражение не требуется, то невозможно заставить компилятор генерировать сообщение об ошибке. Вы ссылаетесь на страницы с участием assert в своем ответе. Как вы думаете, что делает assert, когда условие оценивается как ложное? Это также не приведет к сбою компиляции. На С++ просто нет способа сделать это. - person ; 01.08.2015

Причина, по которой компилятор жалуется, заключается в том, что array неизвестен в то время, когда создается экземпляр функции string_hash<N,n>. static_cast оценивается при создании экземпляра, а не при вызове (даже если это функция constexpr).

Обратите внимание, что для каждой пары значений <N,n> будет создана ровно одна функция. Если вы используете две строки constexpr одинаковой длины, будет использован точно такой же экземпляр string_hash, но в зависимости от аргумента array[N-1] может дать другой результат.

Я буду продолжать искать точный ответ на ваш вопрос, если это необходимо. Однако, в качестве «быстрого исправления», могу ли я предложить изменить хэш-функцию, чтобы она всегда включала в вычисление последний символ, будь то 0 или нет?

Обновление: немного покопавшись, я узнал, что какой-то constexpr_assert — это то, что вам, вероятно, нужно в вашем случае, и в настоящее время он отсутствует в стандарте. Надеюсь, они добавят его в будущем. Вы можете проверить:

person CygnusX1    schedule 01.08.2015