GSL :: пролет & л; Т & GT; и gsl :: span & lt; const T & gt; перегрузки неоднозначны

Основные положения 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и т.д. аргументы.

3

Решение

В соответствии с 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>,

3

Другие решения

Как показано в ответ Саббби 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)));
}
0