Основные положения C ++ продвигает практику с помощью span
.
Проблема в постоянных и изменчивых диапазонах. Вот что я пытался сделать:
auto foo(gsl::span<int>); // 1st
auto foo(gsl::span<const int>); // 2nd
Но они не могут быть вызваны без явного span
приведение / построение аргумента:
std::vector<int> v;
const std::vector<int> cv;
const std::vector<int>& crv = v;
// ambiguous
// want to call 1st
foo(v);
// ambiguous, although 1st is illegal (static_assert kicks in)
// want to call 2nd
foo(cv); // ambiguous
foo(crv); // ambiguous
Как правильно бороться с этим?
Это похоже на то, что должно быть тривиальным, аналог const T&
а также T&
перегрузки, но это не так (или я просто не вижу этого).
Просто чтобы быть на той же странице, foo(gsl::span<int>{v})
это громоздко, и я хочу избежать этого, оставьте вызывающих просто: foo(v)
,
Я обобщил проблему, но на всякий случай это проблема XY, вот что я на самом деле пытаюсь сделать:
auto split(gsl::cstring_span<> str) -> std::vector<gsl::cstring_span<>>;
auto split(gsl::string_span<> str) -> std::vector<gsl::string_span<>>;
и хочу быть вызываемым с [const] char *
, [const] string
и т.д. аргументы.
В соответствии с P0122R1 соответствующий конструктор span
класс это:
template <class Container>
constexpr span(Container& cont);
Так что все ваши 3 примера, к сожалению, плохо сформированы. Второй можно сделать легальным, потребовав удалить этот конструктор из разрешения перегрузки, если только Container::value_type&
конвертируется в span::value_type&
Container
совместим с пролетом.
Даже если мы это сделаем, я не вижу способа разрешить число 1 и 3, поскольку обе перегрузки требуют ровно одного неявного преобразования, определенного пользователем.
Обычный обходной путь — добавить еще один уровень:
template<class T>
auto foo( T && x ) { return foo_impl( as_span(std::forward<T>(x) ) ); }
auto foo_impl(gsl::span<int>); // 1st
auto foo_impl(gsl::span<const int>); // 2nd
Обратите внимание, что as_span
отсутствует в P0122R1, но он реализован в Microsoft GSL. Это работает, потому что он проверяет тип и возвращает span<typename Container::value_type>
,
Как показано в ответ Саббби as_span
это достойное решение span
, Однако нет as_string_span
, что решило бы мою настоящую проблему.
Вот мой просто реализация:
template <class Char_t, gslx::size_t N>
auto as_basic_string_span(gsl::basic_string_span<Char_t, N> str)
-> gsl::basic_string_span<Char_t, N>
{
return str;
}
template <class Char_ptr_t>
auto as_basic_string_span(Char_ptr_t ptr)
-> std::enable_if_t<
stdx::is_pointer_v<Char_ptr_t>,
gsl::basic_string_span<std::remove_pointer_t<Char_ptr_t>>>
{
Expects(ptr != nullptr);
return {ptr, gslx::size_cast(stdx::char_traits_length(ptr))};
}
template <class CharT, gslx::size_t N>
auto as_basic_string_span(stdx::c_array_t<CharT, N>& arr)
-> gsl::basic_string_span<CharT, N - 1>
{
Expects(N > 0 && arr[N - 1] == '\0');
return arr;
}
template <class Char_t, class Traits, class Allocator>
auto as_basic_string_span(std::basic_string<Char_t, Traits, Allocator>& str)
-> gsl::basic_string_span<Char_t>
{
return {const_cast<Char_t*>(str.data()), gslx::size_cast(str.size())};
}
template <class Char_t, class Traits, class Allocator>
auto as_basic_string_span(
const std::basic_string<Char_t, Traits, Allocator>& str)
-> gsl::basic_string_span<const Char_t>
{
return {str.data(), gslx::size_cast(str.size())};
}
template <class Char_t, class Traits, class Allocator>
auto as_basic_string_span(std::basic_string<Char_t, Traits, Allocator>&& str) =
delete;
бонус as_const_basic_string_span
:
template <class Char_t, gslx::size_t N>
auto as_const_basic_string_span(gsl::basic_string_span<Char_t, N> str)
-> gsl::basic_string_span<const Char_t, N>
{
return str;
}
template <class... Args>
auto as_const_basic_string_span(Args&&... args)
-> decltype(as_const_basic_string_span(
as_basic_string_span(std::forward<Args>(args)...)))
{
return as_const_basic_string_span(
as_basic_string_span(std::forward<Args>(args)...));
}
И использование:
template <class CharT>
auto split(gsl::basic_string_span<CharT> str)
-> std::vector<gsl::basic_string_span<CharT>>
template <class T>
auto split(T&& str)
{
return split(as_basic_string_span(std::forward<T>(str)));
}