如何将“basic_string”隐式转换为推导的“basic_string_view”模板?
How to implicitly convert `basic_string` to deduced `basic_string_view` template?
我正在编写一个通用算法,它可以处理任何字符类型的字符串或字符串段——因此我决定使用 std::basic_string_view
和 CharT
的推导模板参数和 Traits
。但是,我很快发现这不允许传递 std::basic_string
对象,因为转换运算符无法对模板推导做出贡献——因此我正在寻找可行的解决方法。
在其核心,我有一个仿函数对象,它接受某种 basic_string_view
以及 CharT
和 Traits
:
的推导模板参数
struct my_algorithm {
template <typename CharT, typename Traits>
auto operator()(std::basic_string_view<CharT,Traits> sv) -> void;
};
我希望能够将 some std::basic_string
的实例传递给此函数,而无需显式指定函数的模板参数:
auto s = std::basic_string<char>{"hello world"}; // some basic_string, doesn't have to be '<char>'
my_algorithm{}(s); // fails to compile
由于无法推断模板重载而产生的错误是:
<source>: In function 'void test()':
<source>:13:19: error: no match for call to '(my_algorithm) (std::__cxx11::basic_string<char>&)'
13 | my_algorithm{}(s);
| ~~~~~~~~~~~~~~^~~
<source>:7:8: note: candidate: 'template<class CharT, class Traits> void my_algorithm::operator()(std::basic_string_view<_CharT, _Traits>)'
7 | auto operator()(std::basic_string_view<CharT,Traits> sv) -> void{}
| ^~~~~~~~
<source>:7:8: note: template argument deduction/substitution failed:
<source>:13:19: note: 'std::__cxx11::basic_string<char>' is not derived from 'std::basic_string_view<_CharT, _Traits>'
13 | my_algorithm{}(s);
| ~~~~~~~~~~~~~~^~~
是否可以在实践中解决这个问题?理想情况下,我正在寻找类似于 statically defined 的行为。
我可以想到几个潜在的解决方法,但每个都有很大的缺点:
为我要支持的每种字符串类型(basic_string
、const CharT*
等)为 my_algorithm::operator()(...)
生成一系列重载。不幸的是,这不能很好地扩展,因为我的代码库有不止一种转换为 string_view
的“字符串”类型。此外,实际代码涉及 2 个字符串参数,因此这会以指数方式增加重载。
在调用 accept
之前始终先手动将 std::basic_string
转换为 std::basic_string_view
(调用方的手动操作):
my_algorithm{}(std::string_view{s});
有没有什么方法可以实现这种转换而无需彻底重载,并且不需要用户显式创建视图?
注意: 我发现另外两个问题 听起来 相似,但它们实际上都没有涉及推导的模板类型。
- basic_string to basic_string_view Implicit Conversion… ughhh why?
最简单的就是让它成为一个更通用的模板
struct my_algorithm {
template <typename StringView>
auto operator()(StringView&& sv) -> void {
using Traits = typename std::remove_cvref_t<StringView>::traits_type;
using CharT = typename std::remove_cvref_t<StringView>::value_type;
/* ... */
}
};
如果你有其他重载,你会想要 SFINAE,这在 C++20 中会更容易,有概念
template <typename T>
concept stringlike = requires {
typename std::remove_cvref_t<T>::traits_type;
typename std::remove_cvref_t<T>::value_type;
}
struct my_algorithm {
template <stringlike StringView>
auto operator()(StringView&& sv) -> void {
using Traits = typename std::remove_cvref_t<StringView>::traits_type;
using CharT = typename std::remove_cvref_t<StringView>::value_type;
/* ... */
}
};
我正在编写一个通用算法,它可以处理任何字符类型的字符串或字符串段——因此我决定使用 std::basic_string_view
和 CharT
的推导模板参数和 Traits
。但是,我很快发现这不允许传递 std::basic_string
对象,因为转换运算符无法对模板推导做出贡献——因此我正在寻找可行的解决方法。
在其核心,我有一个仿函数对象,它接受某种 basic_string_view
以及 CharT
和 Traits
:
struct my_algorithm {
template <typename CharT, typename Traits>
auto operator()(std::basic_string_view<CharT,Traits> sv) -> void;
};
我希望能够将 some std::basic_string
的实例传递给此函数,而无需显式指定函数的模板参数:
auto s = std::basic_string<char>{"hello world"}; // some basic_string, doesn't have to be '<char>'
my_algorithm{}(s); // fails to compile
由于无法推断模板重载而产生的错误是:
<source>: In function 'void test()':
<source>:13:19: error: no match for call to '(my_algorithm) (std::__cxx11::basic_string<char>&)'
13 | my_algorithm{}(s);
| ~~~~~~~~~~~~~~^~~
<source>:7:8: note: candidate: 'template<class CharT, class Traits> void my_algorithm::operator()(std::basic_string_view<_CharT, _Traits>)'
7 | auto operator()(std::basic_string_view<CharT,Traits> sv) -> void{}
| ^~~~~~~~
<source>:7:8: note: template argument deduction/substitution failed:
<source>:13:19: note: 'std::__cxx11::basic_string<char>' is not derived from 'std::basic_string_view<_CharT, _Traits>'
13 | my_algorithm{}(s);
| ~~~~~~~~~~~~~~^~~
是否可以在实践中解决这个问题?理想情况下,我正在寻找类似于 statically defined 的行为。
我可以想到几个潜在的解决方法,但每个都有很大的缺点:
为我要支持的每种字符串类型(
basic_string
、const CharT*
等)为my_algorithm::operator()(...)
生成一系列重载。不幸的是,这不能很好地扩展,因为我的代码库有不止一种转换为string_view
的“字符串”类型。此外,实际代码涉及 2 个字符串参数,因此这会以指数方式增加重载。在调用
accept
之前始终先手动将std::basic_string
转换为std::basic_string_view
(调用方的手动操作):my_algorithm{}(std::string_view{s});
有没有什么方法可以实现这种转换而无需彻底重载,并且不需要用户显式创建视图?
注意: 我发现另外两个问题 听起来 相似,但它们实际上都没有涉及推导的模板类型。
- basic_string to basic_string_view Implicit Conversion… ughhh why?
最简单的就是让它成为一个更通用的模板
struct my_algorithm {
template <typename StringView>
auto operator()(StringView&& sv) -> void {
using Traits = typename std::remove_cvref_t<StringView>::traits_type;
using CharT = typename std::remove_cvref_t<StringView>::value_type;
/* ... */
}
};
如果你有其他重载,你会想要 SFINAE,这在 C++20 中会更容易,有概念
template <typename T>
concept stringlike = requires {
typename std::remove_cvref_t<T>::traits_type;
typename std::remove_cvref_t<T>::value_type;
}
struct my_algorithm {
template <stringlike StringView>
auto operator()(StringView&& sv) -> void {
using Traits = typename std::remove_cvref_t<StringView>::traits_type;
using CharT = typename std::remove_cvref_t<StringView>::value_type;
/* ... */
}
};