【问题标题】:gsl::span<T> and gsl::span<const T> overloads are ambigousgsl::span<T> 和 gsl::span<const T> 重载不明确
【发布时间】:2016-03-07 10:36:32
【问题描述】:

C++ Core Guidelines推广using span的做法。

问题在于 const 和 mutable 范围。这是我试图做的:

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&amp;T&amp; 重载,但它不是(或者我只是没看到)。

为了在同一页面上,foo(gsl::span&lt;int&gt;{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 等参数调用。

【问题讨论】:

  • const std::vector&lt; int &gt;?确定吗?不应该是std::vector&lt; const int &gt;吗?
  • @DevSolar 是的,这就是我的意思。想想const std::vector&lt;int&gt;&amp; crv = v
  • @DevSolar 如果您考虑一下string&amp;const string&amp;,我认为会更清楚。
  • @DevSolar 好主意,但不起作用。编译器发出错误 C2338:C++ 标准禁止 const 元素的容器,因为 allocator 格式不正确。.
  • @WernerHenze:是的,那也是...我只是指出 OP 的第一个示例(span&lt;int&gt;span&lt; const int &gt;)与他的第二个示例(std::vector&lt; int &gt;const std::vector&lt; int &gt;)不匹配) 意图。 (另外,神圣的死灵蝙蝠侠...... :-D)

标签: c++ c++14 cpp-core-guidelines


【解决方案1】:

根据P0122R1span类的相关构造函数是:

template <class Container>
constexpr span(Container& cont);

因此,不幸的是,您的所有 3 个示例都是格式错误的。第二个可以通过要求从重载决议中删除此构造函数来使第二个合法,除非 Container::value_type&amp; 可转换为 span::value_type&amp; 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&lt;typename Container::value_type&gt;

【讨论】:

  • 一个小观察:Container::value_type 仍然是 int for (const vector&lt;int&gt;)。它之所以有效,是因为它确实 span&lt;decltype(*v.data())&gt;
  • 很遗憾没有as_string_span
  • @bolov 很伤心,我猜你必须自己编写代码。
【解决方案2】:

sbabbi's answer 所示as_spanspan 的一个不错的解决方案。但是没有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)));
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-04-07
    • 1970-01-01
    • 1970-01-01
    • 2016-01-23
    • 2020-06-11
    • 2017-02-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多