Использование static_assert
для недопустимых размеров не хорошее решение, потому что оно не очень хорошо работает с SFINAE;т. е. средства TMP, такие как std::is_invocable
и идиома обнаружения , будут возвращать ложные срабатывания для вызовов, которые фактически всегда приводят к ошибке.Гораздо лучше использовать SFINAE для удаления недопустимых размеров из набора перегрузки, в результате чего получается что-то похожее на следующее:
template<std::size_t SX, std::size_t SY,
typename = std::enable_if_t<IsValidArrayXSize<SX>{} && IsValidArrayYSize<SY>{}>>
void PickyArrayHandler(std::array<int, SX> const& x, std::array<int, SY> const& y) {
// Do whatever
}
Сначала нам нужно объявить наши действительные размеры;Я не вижу никакой пользы от более строгой типизации здесь, поэтому для списка целых чисел во время компиляции std::integer_sequence
работает просто отлично и очень легко:
using SizesForArrayX = std::index_sequence<3, 4, 40>;
using SizesForArrayY = std::index_sequence<2, 3, 122>;
Теперь длячерты IsValidArraySize
... Простой способ - использовать расслабленные правила C ++ 14 constexpr
и выполнить простой линейный поиск:
#include <initializer_list>
namespace detail {
template<std::size_t... VSs>
constexpr bool idx_seq_contains(std::index_sequence<VSs...>, std::size_t const s) {
for (auto const vs : {VSs...}) {
if (vs == s) {
return true;
}
}
return false;
}
} // namespace detail
template<std::size_t S>
using IsValidArrayXSize
= std::integral_constant<bool, detail::idx_seq_contains(SizesForArrayX{}, S)>;
template<std::size_t S>
using IsValidArrayYSize
= std::integral_constant<bool, detail::idx_seq_contains(SizesForArrayY{}, S)>;
Online Demo
Однако, если время компиляции вообще вызывает беспокойство, я подозреваю, что будет лучше, если потенциально менее понятно:
namespace detail {
template<bool... Bs>
using bool_sequence = std::integer_sequence<bool, Bs...>;
template<typename, std::size_t>
struct idx_seq_contains;
template<std::size_t... VSs, std::size_t S>
struct idx_seq_contains<std::index_sequence<VSs...>, S>
: std::integral_constant<bool, !std::is_same<bool_sequence<(VSs == S)...>,
bool_sequence<(VSs, false)...>>{}>
{ };
} // namespace detail
template<std::size_t S>
using IsValidArrayXSize = detail::idx_seq_contains<SizesForArrayX, S>;
template<std::size_t S>
using IsValidArrayYSize = detail::idx_seq_contains<SizesForArrayY, S>;
Демонстрация в режиме онлайн
Какой бы путь реализации ни был выбран, использование SFINAE позволяет получать очень хорошие сообщения об ошибках - например, для PickyArrayHandler(std::array<int, 5>{}, std::array<int, 3>{});
, текущийClang 7.0 ToT выдает следующее: какой размер массива недопустим:
error: no matching function for call to 'PickyArrayHandler'
PickyArrayHandler(std::array<int, 5>{}, std::array<int, 3>{});
^~~~~~~~~~~~~~~~~
note: candidate template ignored: requirement 'IsValidArrayXSize<5UL>{}' was not satisfied [with SX = 5, SY = 3]
void PickyArrayHandler(std::array<int, SX> const& x, std::array<int, SY> const& y) {
^