括号内的参数包扩展给出了奇怪的输出
Parameter pack expansion within parentheses gives bizarre output
我正在尝试实现一个函数,它接受可变数量的字符串并转发到打印函数,该函数需要一个 char
指针和 size
每个字符串,交错。
示例:
std::string a = "123";
std::string b = "1234";
forward(a, b); // should call doPrint(a.c_str(), a.size(), b.c_str(), b.size())
我认为以下应该是正确的实现,但即使它编译了,行为也让我感到非常惊讶。
template <class ...Args>
void forward(const Args & ... args) {
doPrint( (args.c_str(), args.size())...);
}
forward(a, b)
调用 doPrint(3, 4)
,而不是 doPrint("123", 3, "1234", 4)
,就好像我写了 doPrint((args.size())...)
。编译器完全忽略对 c_str()
的调用。
我尝试了 g++
、clang
和 icc
,它们都产生了相同的输出。 (args.c_str(), args.size())...
有什么问题?
的确,std::make_tuple(args.c_str(), args.size())...
按预期工作,但假设我无法更改 doPrint
以接受和处理元组。
逗号运算符是一个表达式,其值是最后一个表达式的值。
例如:
int a = (1, 2, 3, 4, 5, 6);
assert(a == 6);
您可以尝试使用元组:
doPrint(std::tuple_cat(std::make_tuple(argc.c_str(), args.size())...));
然后 doPrint
需要更改以使用元组;如果需要,它可以将元组解压回参数包,或者直接使用元组。
解包元组示例:
template <class Tuple, std::size_t ... indices>
doPrint(Tuple t, std::integer_sequence<size_t, indices...>)
{
doPrint(std::get<indices>(t)...);
}
template <class Tuple>
doPrint(Tuple t)
{
doPrint(t, std::make_index_sequence<std::tuple_size<Tuple>::value>());
}
函数名称不明确可能会出现一些问题,因此您可能需要更改这些辅助函数的名称,但希望这足以让您开始。
(args.c_str(), args.size())
是一个逗号分隔的表达式,意思是只有最后一部分(args.size()
)会被传递给函数。
然后它将为每个参数重复此操作,因此它实际上会调用 doPrint
仅使用字符串大小!
您应该更改 doPrint
以改为使用元组,否则您必须使用一些疯狂的模板元编程内容。
我可能会这样做以避免将元组暴露给编程接口:
#include <string>
#include <utility>
#include <tuple>
extern void doPrint(...);
namespace detail {
template<std::size_t...Is, class Tuple>
void forward(std::index_sequence<Is...>, Tuple&& tuple)
{
doPrint(std::get<Is>(tuple)...);
}
}
template<class...Strings>
void forward(Strings&&... strings)
{
detail::forward(std::make_index_sequence<sizeof...(Strings) * 2>(),
std::tuple_cat(std::make_tuple(strings.data(), strings.size())...)
);
}
int main()
{
std::string a = "123";
std::string b = "1234";
forward(a, b); // should call doPrint(a.c_str(), a.size(), b.c_str(), b.size())
}
Jason Turner 在本视频中演示了使用初始化列表扩展可变参数模板的简洁方法:
http://articles.emptycrate.com/2016/05/09/variadic_expansion_wrap_up.html
template< typename ... T >
void do_print(T ... args)
{
(void)std::initializer_list<int> {
(std::cout << args.c_str() << ": "
<< args.size() << "\n", 0)...
};
}
template< typename ... T >
void forward_print(T ... args)
{
do_print(args...);
}
int main(int argc, const char * argv[])
{
std::cout << "Hello, World!\n";
std::string a = "1234";
std::string b = "567";
forward_print(a, b);
return 0;
}
这适用于 g++ -std=c++11
我正在尝试实现一个函数,它接受可变数量的字符串并转发到打印函数,该函数需要一个 char
指针和 size
每个字符串,交错。
示例:
std::string a = "123";
std::string b = "1234";
forward(a, b); // should call doPrint(a.c_str(), a.size(), b.c_str(), b.size())
我认为以下应该是正确的实现,但即使它编译了,行为也让我感到非常惊讶。
template <class ...Args>
void forward(const Args & ... args) {
doPrint( (args.c_str(), args.size())...);
}
forward(a, b)
调用 doPrint(3, 4)
,而不是 doPrint("123", 3, "1234", 4)
,就好像我写了 doPrint((args.size())...)
。编译器完全忽略对 c_str()
的调用。
我尝试了 g++
、clang
和 icc
,它们都产生了相同的输出。 (args.c_str(), args.size())...
有什么问题?
的确,std::make_tuple(args.c_str(), args.size())...
按预期工作,但假设我无法更改 doPrint
以接受和处理元组。
逗号运算符是一个表达式,其值是最后一个表达式的值。
例如:
int a = (1, 2, 3, 4, 5, 6);
assert(a == 6);
您可以尝试使用元组:
doPrint(std::tuple_cat(std::make_tuple(argc.c_str(), args.size())...));
然后 doPrint
需要更改以使用元组;如果需要,它可以将元组解压回参数包,或者直接使用元组。
解包元组示例:
template <class Tuple, std::size_t ... indices>
doPrint(Tuple t, std::integer_sequence<size_t, indices...>)
{
doPrint(std::get<indices>(t)...);
}
template <class Tuple>
doPrint(Tuple t)
{
doPrint(t, std::make_index_sequence<std::tuple_size<Tuple>::value>());
}
函数名称不明确可能会出现一些问题,因此您可能需要更改这些辅助函数的名称,但希望这足以让您开始。
(args.c_str(), args.size())
是一个逗号分隔的表达式,意思是只有最后一部分(args.size()
)会被传递给函数。
然后它将为每个参数重复此操作,因此它实际上会调用 doPrint
仅使用字符串大小!
您应该更改 doPrint
以改为使用元组,否则您必须使用一些疯狂的模板元编程内容。
我可能会这样做以避免将元组暴露给编程接口:
#include <string>
#include <utility>
#include <tuple>
extern void doPrint(...);
namespace detail {
template<std::size_t...Is, class Tuple>
void forward(std::index_sequence<Is...>, Tuple&& tuple)
{
doPrint(std::get<Is>(tuple)...);
}
}
template<class...Strings>
void forward(Strings&&... strings)
{
detail::forward(std::make_index_sequence<sizeof...(Strings) * 2>(),
std::tuple_cat(std::make_tuple(strings.data(), strings.size())...)
);
}
int main()
{
std::string a = "123";
std::string b = "1234";
forward(a, b); // should call doPrint(a.c_str(), a.size(), b.c_str(), b.size())
}
Jason Turner 在本视频中演示了使用初始化列表扩展可变参数模板的简洁方法:
http://articles.emptycrate.com/2016/05/09/variadic_expansion_wrap_up.html
template< typename ... T >
void do_print(T ... args)
{
(void)std::initializer_list<int> {
(std::cout << args.c_str() << ": "
<< args.size() << "\n", 0)...
};
}
template< typename ... T >
void forward_print(T ... args)
{
do_print(args...);
}
int main(int argc, const char * argv[])
{
std::cout << "Hello, World!\n";
std::string a = "1234";
std::string b = "567";
forward_print(a, b);
return 0;
}
这适用于 g++ -std=c++11