gsl::span<T> 和 gsl::span<const T> 重载不明确
gsl::span<T> and gsl::span<const T> overloads are ambigous
C++ Core Guidelines promotes the practice of using span
.
问题在于 const 和可变范围。这就是我试图做的:
auto foo(gsl::span<int>); // 1st
auto foo(gsl::span<const int>); // 2nd
但是如果没有明确的 span
cast/construct 参数就不能调用它们:
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
class 的相关构造函数是:
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中实现。它起作用是因为它检查类型和 return a 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] == '[=10=]');
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)));
}
C++ Core Guidelines promotes the practice of using span
.
问题在于 const 和可变范围。这就是我试图做的:
auto foo(gsl::span<int>); // 1st
auto foo(gsl::span<const int>); // 2nd
但是如果没有明确的 span
cast/construct 参数就不能调用它们:
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
class 的相关构造函数是:
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中实现。它起作用是因为它检查类型和 return a 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] == '[=10=]');
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)));
}