我基本上想要创建一个这样的函数:
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
,但是我想保持函数调用简单,而无需使用{}
。
答案 0 :(得分:5)
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)}... );
}
或类似的东西。
答案 1 :(得分:2)
#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)...);
}
答案 2 :(得分:2)
如果只是想避免不必要的数据复制,请使用前向引用,然后执行显式强制转换(如果仍然需要)。这样就不会复制任何数据,而是将其转发(在您的main.cpp示例中,所有参数都作为const引用传递)
template <typename... strings,
class = std::enable_if_t<are_strings<strings...>::value, void>>
void func(strings&&... args) {
(std::cout << ... << std::string_view{args});
}
答案 3 :(得分:0)
并非完全是您的要求...但是如果您可以为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()
函数是static
内部的foo<N>
个方法,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");
}
答案 4 :(得分:0)
使其分为两个阶段:
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))...);
}