C++ 修改传递给该函数内部可变函数的参数

C++ 修改传递给该函数内部可变函数的参数,c++,pass-by-reference,variadic-templates,variadic-functions,C++,Pass By Reference,Variadic Templates,Variadic Functions,以下代码不编译。如何修改get_number_from_line_variadic中的args变量 非变量版本get_numbers_from_line说明了变量版本应该实现的功能,但是,变量数量可能不同,类型也不同 #include <iostream> #include <sstream> #include <string> template<typename... ArgTypes> void get_numbers_from_line_v

以下代码不编译。如何修改get_number_from_line_variadic中的args变量

非变量版本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;
}

这里的问题是,当直接从带括号的init列表推导类型时,推导的类型是std::initializer_list的特化,std::initializer_list只允许常量访问其元素

更详细地说,基于范围的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
    }
}
如果使用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
}

这里的问题是,当直接从带括号的init列表推导类型时,推导的类型是std::initializer_list的特化,std::initializer_list只允许常量访问其元素

更详细地说,基于范围的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
    }
}
如果使用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
}

第一个重载基函数仅在没有重载的情况下调用。 第二个重载是一个递归变量函数,它将数据包的头部与参数包的尾部分离。这允许仅递归传递尾部,直到尾部变为空

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

第一个重载基函数仅在没有重载的情况下调用。 第二个重载是一个递归变量函数,它将数据包的头部与参数包的尾部分离。这允许仅递归传递尾部,直到尾部变为空

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

args=2。。。;?不工作:错误:在“…”标记之前应该有主表达式。此外,参数将以更复杂的方式更改-而不仅仅是全部更改为2。这只是为了简化问题。ArgTypes&->ArgTypes&&&@M.M如果函数对右值参数没有意义,则不是这样。自C++17以来,args=…=2.args=2。。。;两个legalargs都是=2。。。;?不工作:错误:在“…”标记之前应该有主表达式。此外,参数将以更复杂的方式更改-而不仅仅是全部更改为2。这只是为了简化问题。ArgTypes&->ArgTypes&&&@M.M如果函数对右值参数没有意义,则不是这样。自C++17以来,args=…=2.args=2。。。;如果问题是std::initializer_list只允许const访问,那么如果您需要更复杂的循环,那么auto&a:{&args…}*a=2;工作在这种情况下,指针是常量,但你仍然可以通过它们进行变异。哦,我自己的测试只有在所有参数都是相同类型时才起作用,或者它无法推断初始值设定项列表的类型参数。是的,这将是一种方法。是的,在auto&&il={expressions};或者对于auto&&e:{expressions},从列表中的每个表达式推导出的类型必须是相同的类型,如果这不是故意的限制,这可能是OP函数的另一个问题。@aschepler:谢谢您的回答,但是,变量的修改方式似乎很重要,所以我不得不把真正的代码放在问题里。现在呢?@user3749105相同的交易,只是一个不同的表达式来扩展。编辑我的答案以匹配。如果问题是std::initializer_list只允许常量访问,那么如果需要更复杂的循环,则可以为auto&a:{&args…}*a=2;工作在这种情况下,指针是常量,但你仍然可以通过它们进行变异。哦,我自己的测试只有在所有参数都是相同类型时才起作用,或者它无法推断初始值设定项列表的类型参数。是的,这将是一种方法。是的,在auto&&il={expressions};或者对于auto&&e:{expressions},从列表中的每个表达式推导出的类型必须是相同的类型,如果这不是故意的限制,这可能是OP函数的另一个问题。@aschepler:谢谢您的回答,但是,变量的修改方式似乎很重要,所以我不得不把真正的代码放在问题里。现在呢?@user3749105相同的交易,只是一个不同的表达式来扩展。编辑我的答案以匹配。抱歉,必须修改问题以将真实代码放在那里。太好了!我简化了一点,去掉了旧的部分并接受了作为答案。对不起,我不得不修改这个问题,把真正的代码放在那里。太好了!我简化了一点,去掉了旧的部分,接受了答案。