Перегрузки gsl::span‹T› и gsl::span‹const T› неоднозначны

Основные рекомендации 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 и т. д.


person bolov    schedule 07.03.2016    source источник
comment
const std::vector< int >? Уверен в этом? Разве это не должно быть std::vector< const int >?   -  person DevSolar    schedule 07.03.2016
comment
@DevSolar да, это то, что я имел в виду. подумайте о const std::vector<int>& crv = v.   -  person bolov    schedule 07.03.2016
comment
@DevSolar, если вы думаете о string& и const string&, я думаю, это более понятно.   -  person bolov    schedule 07.03.2016
comment
@DevSolar Хорошая идея, но не работает. Компилятор выдает ошибку C2338: стандарт C++ запрещает контейнеры с элементами const, поскольку распределитель‹const T› имеет неправильный формат..   -  person Werner Henze    schedule 28.02.2020
comment
@WernerHenze: Да, это тоже ... Я просто указал, что первый пример OP (span<int>, span< const int >) не соответствует его второму (std::vector< int >, const std::vector< int >) намерению. (Также святой некро Бэтмен... :-D )   -  person DevSolar    schedule 28.02.2020


Ответы (2)


Согласно 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>.

person sbabbi    schedule 07.03.2016
comment
небольшое наблюдение: Container::value_type по-прежнему int для (const vector<int>). Это работает, потому что это span<decltype(*v.data())> - person bolov; 08.03.2016
comment
к сожалению нет as_string_span - person bolov; 08.03.2016
comment
@bolov довольно грустный, я думаю, вам придется кодировать его самостоятельно. - person sbabbi; 08.03.2016

Как показано в ответе sbabbi, 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)));
}
person bolov    schedule 08.03.2016