修改传递给该函数内部可变参数函数的参数
Modify arguments passed to a variadic function inside that function
以下代码无法编译。如何修改 get_numbers_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;
}
这里的问题是,当一个类型直接从花括号初始化列表推导时,推导的类型是 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
}
第一个重载(基函数)只有在没有parameter pack expansion时才会被调用。
第二个重载是一个递归可变参数函数,它将包的头部与尾部(参数包的其余部分)分开。这使得只递归地传递尾部直到它变空。
#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;
}
Fold expression 版本 (C++17 起):
template<typename... Ts>
void get_numbers_from_line(std::string line, Ts&&... args)
{
std::istringstream iss(line);
(iss >> ... >> std::forward<Ts>(args));
}
以下代码无法编译。如何修改 get_numbers_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;
}
这里的问题是,当一个类型直接从花括号初始化列表推导时,推导的类型是 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
}
第一个重载(基函数)只有在没有parameter pack expansion时才会被调用。 第二个重载是一个递归可变参数函数,它将包的头部与尾部(参数包的其余部分)分开。这使得只递归地传递尾部直到它变空。
#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;
}
Fold expression 版本 (C++17 起):
template<typename... Ts>
void get_numbers_from_line(std::string line, Ts&&... args)
{
std::istringstream iss(line);
(iss >> ... >> std::forward<Ts>(args));
}