Является ли функция базового случая обязательной или она может быть автоматически синтезирована? - PullRequest
0 голосов
/ 30 января 2019

В следующем коде:

void print()
{
    // This is our base case fn
    ;; // Do nothing
}

template <typename type1, typename... argspack>
void print(type1 a, argspack... args_rest)
{
    cout << a << ((sizeof...(args_rest) != 0) ? "," : "\n");
    print(args_rest...); // I guess this recursive call is inevitable
}

Если рекурсивный вызов функции с переменными числами неизбежен, функция базового случая также неизбежна.Если да, то есть ли языковая функция, возможно, поставляемая с современным c ++, которая помогает программисту уйти без написания базовой функции?

Ответы [ 4 ]

0 голосов
/ 30 января 2019

С одной функцией в C ++ 11:

template <typename... Ts>
void print(Ts... args)
{
    const char* sep = "";
    const int dummy[] = {((std::cout << sep << args), (sep = ", "), 0)..., 0};
    static_cast<void>(dummy); // Avoid warning for unused variable
    std::cout << "\n";
}

трюк с фиктивным массивом можно заменить выражением сгиба в C ++ 17:

template <typename... Ts>
void print(Ts... args)
{
    const char* sep = "";
    (((std::cout << sep << args), (sep = ", ")), ...);
    std::cout << "\n";
}
0 голосов
/ 30 января 2019

Манекен - это один из способов.Другой - сделать функцию с одним аргументом, которая действительно работает:

template<typename T>
void print(T a)
{
  std::cout << a;
}

template <typename type1, typename... argspack>
void print(type1 a, argspack... args_rest)
{
    print(a);
    std::cout << ((sizeof...(args_rest) != 0) ? "," : "\n");
    print(args_rest...); // I guess this recursive call is inevitable
}

Преимущество этого подхода в том, что он также предоставляет точку настройки.Если некоторый тип хочет предоставить свою собственную реализацию print, все, что ему нужно сделать, это записать перегрузку.ADL найдет его, а разрешение перегрузки будет в пользу.

Чтобы быть совершенно ясным, вы должны написать базовый вариант самостоятельно.Язык не считает эти две функции связанными, за исключением перегрузок с одним и тем же именем.Он даже не знает, что нужно , чтобы быть базовым случаем, это наше логическое требование.

0 голосов
/ 30 января 2019

Другой (немного сложный) способ, который позволяет избежать рекурсии и запятой:

#include <iostream>
#include <tuple>

struct linefeed {};

template<typename...Args>
void print(Args&&... args) 
{
    const char* sep = "";
    auto print_with_sep = [&sep](auto& os, auto& arg)
    {
        if constexpr (std::is_same<std::decay_t<decltype(arg)>, linefeed>())
        {
            sep = "";
            os << '\n';
        }
        else
        {
            os << sep << arg;
            sep = ",";
        }
    };

    auto print_all = [&](auto&&...things)
    {
        (print_with_sep(std::cout, things), ...);
    };

    print_all(args..., linefeed());
}

int main()
{
    print(1,2,3,4,5, "hello");
    print("world", 5,4,3,2,1);
}

ожидаемый результат:

1,2,3,4,5,hello
world,5,4,3,2,1

https://coliru.stacked -crooked.com / a/ 770912eee67d04ac

0 голосов
/ 30 января 2019

Вы можете сослаться на Fold Expression , которая поддерживается на C ++ 17.

Я придумал код, почти похожий на ваш код, но у него есть запятая.

template<typename... argspack>
void print(argspack&&... args) {
  ((cout << args << ","), ...) << "\n";
}

Я не уверен, что есть способ получить то же самое с вашим кодом, используя Fold Expression.Поскольку у нас sizeof...(args) всегда есть начальный размер в этой версии.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...