How to properly implement a function with variadic number of std::string_view arguments?

how to define a variadic number of arguments of the same type -- part 2
c variadic arguments
c++ variadic templates
c forward variadic arguments
c variadic arguments of same type
variadic template template parameter
homogeneous variadic function parameters
std::enable_if
Desired behavior

What I basically want is to create a function like this:

void func(std::string_view... args)
{
    (std::cout << ... << args);
}

It should be able to work only with classes that are convertible to std::string_view.

Example:

int main()
{
    const char* tmp1 = "Hello ";
    const std::string tmp2 = "World";
    const std::string_view tmp3 = "!";

    func(tmp1, tmp2, tmp3, "\n");

    return 0;
}

should print: Hello World!


Accomplished behavior

So far, I got here:

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;
}

This actually works as expected, but there is still one big problem.


Problem

Only classes that are convertible to std::string_view can be used in this function and that's great. However, even though classes are convertible, they are not converted to std::string_view!

This leads to needless copying of data(for example when std::string is passed as argument).


Question

Is there a way to force implicit conversion of variadic arguments to std::string_view?


Note

I know about std::initializer_list, but I would like to keep function call simple, without {}.

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)}... );
}

or somesuch.

How to Define A Variadic Number of Arguments of the Same Type , How to Define A Variadic Number of Arguments of the Same Type – Part In the particular case of std::string we can use std::string_view as in We could also rely a little on the implementation of the function for this. If this expression is created successfully, it means that all the parameters are convertible  Problem. Only classes that are convertible to std::string_view can be used in this function and that's great. However, even though classes are convertible, they are not converted to std::string_view!

#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)...);
}

DEMO

Non-terminal variadic template parameters, can appear before the last function parameter, they do not get properly deduced when they do. But what if your log function accepts a variadic number of arguments? void log(std::string_view, auto args, std::source_location loc Bruno Manganelli is notably responsible for the clang implementation. In the particular case of std::string we can use std::string_view as in the above snippet, in order to avoid copying the arguments into the std::initializer_list. In the general case we’d have to make a copy though (at least I can’t see how to avoid the copy, if you know do leave a comment below). The call site looks like this:

If you simply want to avoid needless copying of data, use a forward reference and then perform explicit casts (if still required). This way no data is copied but forwarded (in your main.cpp example, all params are passed as const references)

template <typename... strings,
          class = std::enable_if_t<are_strings<strings...>::value, void>>
void func(strings&&... args) {
  (std::cout << ... << std::string_view{args});
}

`constexpr` function parameters – Michael Park, constexpr void f(std::size_t n) { static_assert(n == 42, ""); // not allowed. } This function is not allowed because n could be a runtime value, in which Initially, I was concerned about the potential of introducing too many types. to capture and pass around compile-time strings, or even a std::string_view . There might be a way to use std::reference_wrapper to get that behaviour; as for types that don't implement < - I'd call that a bug in the type (but it's easy to pass a custom comparator). I'll write this in an answer. \$\endgroup\$ – Toby Speight Jun 13 '19 at 16:06

Not exactly what you asked... but if you can set a superior limit for a the length of args... (9 in following example) I propose the following solution: a foo<N> struct that inherit N func() static function that accepting 0, 1, 2, ..., N std::string_view.

This way, func() function are accepting what is convertible to std::string_view and all argument are converted to std::string_view.

That is exactly

void func(std::string_view... args)
{ (std::cout << ... << args); }

with the difference that func() functions are static methods inside foo<N>, that there is a limit in args... length and that there is a func() method for every supported length.

The full example is the following.

#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");
 }

Writing a function template with a variable number of arguments , C++11 introduced variadic templates, which are templates with a variable number of arguments that make it possible to write both type-safe function templates  Variadic templates. One of the new features of C++11 is variadic templates.Finally, there's a way to write functions that take an arbitrary number of arguments in a type-safe way and have all the argument handling logic resolved at compile-time, rather than run-time.

Make it a two-stage production:

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))...);
}

C++17 - Avoid Copying with std::string_view, std::string and std::string_view have both a method substr. I fill the std::vector<​int> in line 37 with access number (10'000'000) of values in the  C++ Variadic Lambdas For Beginners. Made with slight assistance by @DynamicSquid. I know not everyone will agree with this, and an even easier interface may exist, but let us continue regardless. Sometimes, you want to use the same function name with different output depending on the number of arguments passed.

Perfect Forwarding, If a function templates forward its arguments without changing their lvalue or rvalue The value 5 and the constructor call are std::string("Rvalue") rvalues because you Variadic Templates are templates that can get an arbitrary number of arguments. I'm confused, do you mean either/or or neither/nor? Variadic templates can also be used to create functions that take variable number of arguments. They are often the better choice because they do not impose restrictions on the types of the arguments, do not perform integral and floating-point promotions, and are type safe.

C++ 17 New Features and Trick, Multiple return values from functions are not a new concept in programming 1 #​include <iostream> 2 #include <tuple> // std::tie 3 4 const double PI does not work correctly"); constexpr int resultAdd32 = Add32Lambda(10); parameter pack (variadic template get variable number of arguments and is  In mathematics and in computer programming, a variadic function is a function of indefinite arity, i.e., one which accepts a variable number of arguments. Support for variadic functions differs widely among programming languages. The term variadic is a neologism, dating back to 1936. The term was not widely used until the 1970s.

Variadic functions, Variadic functions are functions (e.g. std::printf) which take a variable number of arguments. To declare a variadic function, an ellipsis is used as the last  You can easily implement indexed access to the arguments, that is %N meaning print the N-th argument. You can easily specialize the printers for a specific subset of arguments, by adding multiple make_argument overloads returning dedicated printers. For example, consider implementing a Python-like format language:

Comments
  • Would passing the arguments via (const?) reference be an option?
  • It is certainly my backup plan :D I am not sure how std::string_view responds to references since it adds another redirection to the class that basically is just a reference. Nevertheless, I am generally interested if there is a way of forcing implicit conversion to variadic arguments. Anyway, thanks for suggestion!
  • If func has no return value and you do not need to take its address you can do this wandbox.org/permlink/vx7KlBhBhU6icMiF
  • Thank you for the help, but godbolt.org shows that your solution actually generates more assembly. Const reference looks like a better solution, unless you know some example(of main program) where your function would outperform the given one?
  • @Iskustvo I'm just suggesting that you could probably get away with template <typename... Ts> void func(Ts&&...) { /*code*/ }, I suspect you don't need to force that conversion to string_view at all, though I don't know your exact use case
  • This looks promising. It still generates a bit more assembly than just adding const& to arguments, at least on few examples I tested it. There is compiler error in static_assert, so I removed it. Can you please explain this solution a bit?
  • @isku turn inlining up, forcing on non-impl idealy. I perfect forward to a cast to string view. Add some brackets and a quoted string to static assert of your compiler is c++11.