Как правильно реализовать функцию с переменным числом аргументов std :: string_view? - PullRequest
0 голосов
/ 08 декабря 2018

Желаемое поведение

Я хочу создать такую ​​функцию:

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, но я бы хотел, чтобы вызов функции был простым, без {}.

Ответы [ 5 ]

0 голосов
/ 09 декабря 2018

Если вы просто хотите избежать ненужного копирования данных, используйте прямую ссылку, а затем выполните явное приведение (если оно все еще требуется).Таким образом, данные не копируются, а пересылаются (в вашем примере 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});
}
0 голосов
/ 09 декабря 2018

Сделать это в два этапа:

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))...);
}
0 голосов
/ 09 декабря 2018
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)}... );
}

или что-то подобное.

0 голосов
/ 09 декабря 2018
#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

0 голосов
/ 09 декабря 2018

Не совсем то, что вы просили ... но если вы можете установить верхний предел для длины 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");
 }
...