Изменить аргументы, передаваемые в функцию с переменным числом внутри этой функции - PullRequest
0 голосов
/ 05 июня 2018

Следующий код не компилируется.Как я могу изменить args переменные внутри get_numbers_from_line_variadic?

Невариадная версия get_numbers_from_line показывает, чего должна достичь переменная, однако с переменным числом аргументов, возможно, с разными типами.

#include <iostream>
#include <sstream>
#include <string>

template<typename... ArgTypes>
void get_numbers_from_line_variadic(std::string line, ArgTypes&... args)
{
   std::istringstream iss(line);

   for (auto& arg : {args...})
      iss >> arg;
}

void get_numbers_from_line(std::string line, int& a, int& b)
{
    std::istringstream iss(line);
    iss >> a;
    iss >> b;
}

int main()
{
    int a, b;
    get_numbers_from_line("1 2", a, b);
    get_numbers_from_line_variadic("1 2", a, b);

    std::cout << "a = " << a << std::endl;
    std::cout << "b = " << b << std::endl;
}

Ответы [ 2 ]

0 голосов
/ 05 июня 2018

Первая перегрузка (базовая функция) вызывается только при отсутствии расширения пакета параметров .Вторая перегрузка - это рекурсивная переменная функция, которая отделяет голову пакета от хвоста (остальная часть пакета параметров).Это позволяет проходить только хвост рекурсивно, пока он не станет пустым.

#include <iostream>
#include <sstream>
#include <string>

void get_numbers_from_line(std::istringstream&){} // base function

template<typename T, typename... Ts>
void get_numbers_from_line(std::istringstream& iss, T&& head, Ts&&... tail) // recursive variadic function
{
    iss >> head;
    get_numbers_from_line(iss, std::forward<Ts>(tail)...);
}

template<typename... Ts>
void get_numbers_from_line(std::string line, Ts&&... args)
{
    std::istringstream iss(line);
    get_numbers_from_line(iss, std::forward<Ts>(args)...);
}

int main()
{
    double a;
    int b, c;
    get_numbers_from_line("-0.1 2 3", a, b, c);

    std::cout << "a = " << a << std::endl;
    std::cout << "b = " << b << std::endl;
    std::cout << "c = " << c << std::endl;
}

Выражение сгиба версия (начиная с C ++ 17):

template<typename... Ts>
void get_numbers_from_line(std::string line, Ts&&... args)
{
    std::istringstream iss(line);
    (iss >> ... >> std::forward<Ts>(args));
}
0 голосов
/ 05 июня 2018

Проблема здесь в том, что когда тип выводится непосредственно из списка фигурных скобок инициализации, выведенный тип является специализацией std::initializer_list, а std::initializer_list разрешает const только доступ к его элементам.

Более подробно, ваш оператор for на основе диапазона похож на цикл:

{
    auto&& range = {args...};      // std::initializer_list<int>&&
    auto iter = range.begin();     // const int*
    auto end  = range.end();       // const int*
    for (; iter != end; ++iter) {
        auto& arg = *iter;         // const int&
        iss >> arg;                // ERROR
    }
}

, поскольку std::initializer_list<T>::iterator равен const T*.

Вам понадобитсядля более точной обработки параметров функции.

Как отмечено в комментариях, если вы используете C ++ 17 (или более позднюю версию), вы можете использовать выражение сгиба.

{
    std::istringstream iss(line);
    (iss >> ... >> args);
}

Еслииспользуя C ++ 11 или C ++ 14, вместо этого можно использовать трюк инициализации фиктивного массива:

{
    std::istringstream iss(line);

    // Note (expr, 0) to discard expression result and supply int
    // for the array, and final 0 in case sizeof...(args)==0
    int dummy[] = { (iss >> args, 0)..., 0 };
    static_cast<void>(dummy); // avoid unused variable warning
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...