如何正确实现具有可变数量 std::string_view 个参数的函数?
How to properly implement a function with variadic number of std::string_view arguments?
期望的行为
我基本上想要的是创建一个这样的函数:
void func(std::string_view... args)
{
(std::cout << ... << args);
}
它应该能够 仅 与可转换为 std::string_view
.
的 类 一起工作
示例:
int main()
{
const char* tmp1 = "Hello ";
const std::string tmp2 = "World";
const std::string_view tmp3 = "!";
func(tmp1, tmp2, tmp3, "\n");
return 0;
}
应该打印:Hello World!
完成的行为
到目前为止,我到达这里:
template<typename... types>
using are_strings = std::conjunction<std::is_convertible<types, std::string_view>...>;
template<typename... strings, class = std::enable_if_t<are_strings<strings...>::value, void>>
void func(strings... args)
{
(std::cout << ... << args);
}
int main()
{
const char* tmp1 = "Hello ";
const std::string tmp2 = "World";
const std::string_view tmp3 = "!";
func(tmp1, tmp2, tmp3, "\n");
return 0;
}
这确实符合预期,但仍然存在一个大问题。
问题
只有可以转换为 std::string_view
的 类 才能在这个函数中使用,这很好。
但是,即使 类 可以转换,它们 也不会 转换为 std::string_view
!
这会导致不必要的数据复制(例如,当 std::string
作为参数传递时)。
问题
有没有办法强制将可变参数隐式转换为 std::string_view
?
备注
我知道 std::initializer_list
,但我想保持函数调用简单,没有 {}
。
不完全是你问的...但是如果你可以为 args...
的长度设置一个上限(在下面的例子中是 9)我提出以下解决方案: foo<N>
结构继承 N
func()
接受 0, 1, 2, ..., N std::string_view
.
的静态函数
这样,func()
函数接受可转换为 std::string_view
的内容,所有参数都转换为 std::string_view
.
正是
void func(std::string_view... args)
{ (std::cout << ... << args); }
不同之处在于 func()
函数是 foo<N>
中的 static
方法,args...
长度有限制并且有一个 func()
每个支持的长度的方法。
完整示例如下。
#include <string>
#include <utility>
#include <iostream>
#include <type_traits>
template <std::size_t ... Is>
constexpr auto getIndexSequence (std::index_sequence<Is...> is)
-> decltype(is);
template <std::size_t N>
using IndSeqFrom = decltype(getIndexSequence(std::make_index_sequence<N>{}));
template <typename T, std::size_t>
struct getType
{ using type = T; };
template <typename, typename>
struct bar;
template <typename T, std::size_t ... Is>
struct bar<T, std::index_sequence<Is...>>
{
static void func (typename getType<T, Is>::type ... args)
{ (std::cout << ... << args); }
};
template <std::size_t N, typename = std::string_view,
typename = IndSeqFrom<N>>
struct foo;
template <std::size_t N, typename T, std::size_t ... Is>
struct foo<N, T, std::index_sequence<Is...>> : public bar<T, IndSeqFrom<Is>>...
{ using bar<T, IndSeqFrom<Is>>::func ...; };
int main ()
{
const char* tmp1 = "Hello ";
const std::string tmp2 = "World";
const std::string_view tmp3 = "!";
foo<10u>::func(tmp1, tmp2, tmp3, "\n");
}
namespace impl{
template<class...SVs>
void func(SVs... svs){
static_assert( (std::is_same< SVs, std::string_view >{} && ...) );
// your code here
}
}
template<class...Ts,
std::enable_if_t< (std::is_convertible<Ts, std::string_view >{}&&...), bool > =true
>
void func( Ts&&...ts ){
return impl::func( std::string_view{std::forward<Ts>(ts)}... );
}
或类似的东西。
#include <string_view>
#include <utility>
template <typename>
using string_view_t = std::string_view;
template <typename... Ts>
void func_impl(string_view_t<Ts>... args)
{
(std::cout << ... << args);
}
template <typename... Ts>
auto func(Ts&&... ts)
-> decltype(func_impl<Ts...>(std::forward<Ts>(ts)...))
{
return func_impl<Ts...>(std::forward<Ts>(ts)...);
}
分两步制作:
template <class... Args>
std::enable_if_t<... && std::is_same<Args, std::string_view>()>
func(Args... args)
{
(std::cout << ... << args);
}
template <class... Args>
auto func(Args&&... args)
-> std::enable_if_t<... || !std::is_same<std::decay_t<Args>, std::string_view>(),
decltype(func(std::string_view(std::forward<Args>(args))...))>
{
func(std::string_view(std::forward<Args>(args))...);
}
如果您只是想避免不必要的数据复制,请使用前向引用,然后执行显式转换(如果仍然需要)。这样就没有数据被复制但被转发(在你的main.cpp示例中,所有参数都作为常量引用传递)
template <typename... strings,
class = std::enable_if_t<are_strings<strings...>::value, void>>
void func(strings&&... args) {
(std::cout << ... << std::string_view{args});
}
期望的行为
我基本上想要的是创建一个这样的函数:
void func(std::string_view... args)
{
(std::cout << ... << args);
}
它应该能够 仅 与可转换为 std::string_view
.
示例:
int main()
{
const char* tmp1 = "Hello ";
const std::string tmp2 = "World";
const std::string_view tmp3 = "!";
func(tmp1, tmp2, tmp3, "\n");
return 0;
}
应该打印:Hello World!
完成的行为
到目前为止,我到达这里:
template<typename... types>
using are_strings = std::conjunction<std::is_convertible<types, std::string_view>...>;
template<typename... strings, class = std::enable_if_t<are_strings<strings...>::value, void>>
void func(strings... args)
{
(std::cout << ... << args);
}
int main()
{
const char* tmp1 = "Hello ";
const std::string tmp2 = "World";
const std::string_view tmp3 = "!";
func(tmp1, tmp2, tmp3, "\n");
return 0;
}
这确实符合预期,但仍然存在一个大问题。
问题
只有可以转换为 std::string_view
的 类 才能在这个函数中使用,这很好。
但是,即使 类 可以转换,它们 也不会 转换为 std::string_view
!
这会导致不必要的数据复制(例如,当 std::string
作为参数传递时)。
问题
有没有办法强制将可变参数隐式转换为 std::string_view
?
备注
我知道 std::initializer_list
,但我想保持函数调用简单,没有 {}
。
不完全是你问的...但是如果你可以为 args...
的长度设置一个上限(在下面的例子中是 9)我提出以下解决方案: foo<N>
结构继承 N
func()
接受 0, 1, 2, ..., N std::string_view
.
这样,func()
函数接受可转换为 std::string_view
的内容,所有参数都转换为 std::string_view
.
正是
void func(std::string_view... args)
{ (std::cout << ... << args); }
不同之处在于 func()
函数是 foo<N>
中的 static
方法,args...
长度有限制并且有一个 func()
每个支持的长度的方法。
完整示例如下。
#include <string>
#include <utility>
#include <iostream>
#include <type_traits>
template <std::size_t ... Is>
constexpr auto getIndexSequence (std::index_sequence<Is...> is)
-> decltype(is);
template <std::size_t N>
using IndSeqFrom = decltype(getIndexSequence(std::make_index_sequence<N>{}));
template <typename T, std::size_t>
struct getType
{ using type = T; };
template <typename, typename>
struct bar;
template <typename T, std::size_t ... Is>
struct bar<T, std::index_sequence<Is...>>
{
static void func (typename getType<T, Is>::type ... args)
{ (std::cout << ... << args); }
};
template <std::size_t N, typename = std::string_view,
typename = IndSeqFrom<N>>
struct foo;
template <std::size_t N, typename T, std::size_t ... Is>
struct foo<N, T, std::index_sequence<Is...>> : public bar<T, IndSeqFrom<Is>>...
{ using bar<T, IndSeqFrom<Is>>::func ...; };
int main ()
{
const char* tmp1 = "Hello ";
const std::string tmp2 = "World";
const std::string_view tmp3 = "!";
foo<10u>::func(tmp1, tmp2, tmp3, "\n");
}
namespace impl{
template<class...SVs>
void func(SVs... svs){
static_assert( (std::is_same< SVs, std::string_view >{} && ...) );
// your code here
}
}
template<class...Ts,
std::enable_if_t< (std::is_convertible<Ts, std::string_view >{}&&...), bool > =true
>
void func( Ts&&...ts ){
return impl::func( std::string_view{std::forward<Ts>(ts)}... );
}
或类似的东西。
#include <string_view>
#include <utility>
template <typename>
using string_view_t = std::string_view;
template <typename... Ts>
void func_impl(string_view_t<Ts>... args)
{
(std::cout << ... << args);
}
template <typename... Ts>
auto func(Ts&&... ts)
-> decltype(func_impl<Ts...>(std::forward<Ts>(ts)...))
{
return func_impl<Ts...>(std::forward<Ts>(ts)...);
}
分两步制作:
template <class... Args>
std::enable_if_t<... && std::is_same<Args, std::string_view>()>
func(Args... args)
{
(std::cout << ... << args);
}
template <class... Args>
auto func(Args&&... args)
-> std::enable_if_t<... || !std::is_same<std::decay_t<Args>, std::string_view>(),
decltype(func(std::string_view(std::forward<Args>(args))...))>
{
func(std::string_view(std::forward<Args>(args))...);
}
如果您只是想避免不必要的数据复制,请使用前向引用,然后执行显式转换(如果仍然需要)。这样就没有数据被复制但被转发(在你的main.cpp示例中,所有参数都作为常量引用传递)
template <typename... strings,
class = std::enable_if_t<are_strings<strings...>::value, void>>
void func(strings&&... args) {
(std::cout << ... << std::string_view{args});
}