没有第一个的 C++ 可变参数
C++ Variadic parameters with out first one
是否可以使用不指定第一个参数的可变参数?
例如:
这段代码非常好:
void something(const char* layoutAndButton, ...)
{
va_list ap;
va_start(ap, layoutAndButton);
std::map<std::string, std::string> data;
while (*layoutAndButton != '[=10=]') {
std::string layout = va_arg(ap, const char*);
++layoutAndButton;
std::string button = va_arg(ap, const char*);
++layoutAndButton;
data.insert(std::make_pair(layout, button));
}
for (auto const& x : data)
{
std::cout << x.first << ':' << x.second << std::endl;
}
va_end(ap);
}
但我想以这种方式让 something 发挥作用:
void something(const char*...)
有没有可能做那样的事情?然后访问成员?如果是,怎么做?
谢谢
以下是使用 C++ 可变参数模板的方法。您可以使用任意偶数个 const char*
参数调用 something()
,例如something("k1", "v1", "k2", "v2")
。它将通过递归调用 buildMap()
函数,根据这些参数构建一个 map<string,string>
,然后调用 useMap()
来执行您希望对地图完成的任何实际工作。
void buildMap(std::map<std::string, std::string>& data)
{
}
template<typename... Args>
void buildMap(
std::map<std::string, std::string>& data,
const char* layout,
const char* button,
Args... args)
{
data.insert(std::make_pair(layout, button));
buildMap(data, args...);
}
void useMap(std::map<std::string, std::string>& data)
{
// TODO: do something here
}
template<typename... Args>
void something(Args... args) {
std::map<std::string, std::string> data;
buildMap(data, args...);
useMap(data);
}
如评论中所述std::initializer_list
似乎可以做到
void something(std::initializer_list<std::pair<std::string, std::string>> layoutAndButtons)
{
// std::map<std::string, std::string> m(layoutAndButtons); // potentially
for (auto const& p : layoutAndButtons) {
std::cout << p.first << ':' << p.second << std::endl;
}
}
甚至,如果你真的需要一张地图:
void something(const std::map<std::string, std::string>& layoutAndButtons)
for (auto const& p : layoutAndButtons) {
std::cout << p.first << ':' << p.second << std::endl;
}
}
用法类似于:
something({{"Some", "things"}, {"are", "done"}});
如果你真的想要可变参数模板,我建议:
template<typename... Args>
void something(Args... args)
{
static_assert(sizeof...(Args) % 2 == 0, "wrong number of argument");
const char* layoutAndButtons[] = {args...};
std::map<std::string, std::string> m;
for (auto it = std::begin(layoutAndButtons);
it != std::end(layoutAndButtons);
it += 2) {
auto layout = *it;
auto button = *(it + 1);
m.emplace(layout, button);
}
for (auto const& p : m)
{
std::cout << p.first << ':' << p.second << std::endl;
}
}
如果您可以使用 C++14(std::index_sequence
和 std::make_index_sequence
),您可以避免递归将 args...
包装在 std::tuple
中,生成索引列表并使用索引和 std::get()
.
初始化 std::map
我的意思是:如果你写一个辅助函数如下
template <typename ... Args, std::size_t ... Is>
std::map<std::string, std::string> getMap
(std::tuple<Args...> const & t, std::index_sequence<Is...> const &)
{ return { {std::get<(Is<<1)>(t), std::get<(Is<<1)+1U>(t)} ... }; }
在something()
中你可以如下初始化data
auto data = getMap(std::tie(args...),
std::make_index_sequence<(sizeof...(Args)>>1)>{});
但我也建议在这一行之前加上一个static_assert()
来检查args...
的数量是否是偶数;像
static_assert( (sizeof...(Args) & 1U) == 0U, "#Args is odd!");
以下是一个完整的工作示例
#include <map>
#include <tuple>
#include <iostream>
#include <type_traits>
template <typename ... Args, std::size_t ... Is>
std::map<std::string, std::string> getMap
(std::tuple<Args...> const & t, std::index_sequence<Is...> const &)
{ return { {std::get<(Is<<1)>(t), std::get<(Is<<1)+1U>(t)} ... }; }
template <typename... Args>
void something (Args... args)
{
static_assert( (sizeof...(Args) & 1U) == 0U, "#Args is odd!");
auto data = getMap(std::tie(args...),
std::make_index_sequence<(sizeof...(Args)>>1)>{});
for ( auto const & p : data )
std::cout << '[' << p.first << ',' << p.second << ']';
std::cout << std::endl;
}
int main ()
{
something("k1", "v1", "k2", "v2", "k3", "v3"); // compile
//something("k1", "v1", "k2", "v2", "odd!"); // static_assert() failure
}
是否可以使用不指定第一个参数的可变参数?
例如:
这段代码非常好:
void something(const char* layoutAndButton, ...)
{
va_list ap;
va_start(ap, layoutAndButton);
std::map<std::string, std::string> data;
while (*layoutAndButton != '[=10=]') {
std::string layout = va_arg(ap, const char*);
++layoutAndButton;
std::string button = va_arg(ap, const char*);
++layoutAndButton;
data.insert(std::make_pair(layout, button));
}
for (auto const& x : data)
{
std::cout << x.first << ':' << x.second << std::endl;
}
va_end(ap);
}
但我想以这种方式让 something 发挥作用:
void something(const char*...)
有没有可能做那样的事情?然后访问成员?如果是,怎么做?
谢谢
以下是使用 C++ 可变参数模板的方法。您可以使用任意偶数个 const char*
参数调用 something()
,例如something("k1", "v1", "k2", "v2")
。它将通过递归调用 buildMap()
函数,根据这些参数构建一个 map<string,string>
,然后调用 useMap()
来执行您希望对地图完成的任何实际工作。
void buildMap(std::map<std::string, std::string>& data)
{
}
template<typename... Args>
void buildMap(
std::map<std::string, std::string>& data,
const char* layout,
const char* button,
Args... args)
{
data.insert(std::make_pair(layout, button));
buildMap(data, args...);
}
void useMap(std::map<std::string, std::string>& data)
{
// TODO: do something here
}
template<typename... Args>
void something(Args... args) {
std::map<std::string, std::string> data;
buildMap(data, args...);
useMap(data);
}
如评论中所述std::initializer_list
似乎可以做到
void something(std::initializer_list<std::pair<std::string, std::string>> layoutAndButtons)
{
// std::map<std::string, std::string> m(layoutAndButtons); // potentially
for (auto const& p : layoutAndButtons) {
std::cout << p.first << ':' << p.second << std::endl;
}
}
甚至,如果你真的需要一张地图:
void something(const std::map<std::string, std::string>& layoutAndButtons)
for (auto const& p : layoutAndButtons) {
std::cout << p.first << ':' << p.second << std::endl;
}
}
用法类似于:
something({{"Some", "things"}, {"are", "done"}});
如果你真的想要可变参数模板,我建议:
template<typename... Args>
void something(Args... args)
{
static_assert(sizeof...(Args) % 2 == 0, "wrong number of argument");
const char* layoutAndButtons[] = {args...};
std::map<std::string, std::string> m;
for (auto it = std::begin(layoutAndButtons);
it != std::end(layoutAndButtons);
it += 2) {
auto layout = *it;
auto button = *(it + 1);
m.emplace(layout, button);
}
for (auto const& p : m)
{
std::cout << p.first << ':' << p.second << std::endl;
}
}
如果您可以使用 C++14(std::index_sequence
和 std::make_index_sequence
),您可以避免递归将 args...
包装在 std::tuple
中,生成索引列表并使用索引和 std::get()
.
std::map
我的意思是:如果你写一个辅助函数如下
template <typename ... Args, std::size_t ... Is>
std::map<std::string, std::string> getMap
(std::tuple<Args...> const & t, std::index_sequence<Is...> const &)
{ return { {std::get<(Is<<1)>(t), std::get<(Is<<1)+1U>(t)} ... }; }
在something()
中你可以如下初始化data
auto data = getMap(std::tie(args...),
std::make_index_sequence<(sizeof...(Args)>>1)>{});
但我也建议在这一行之前加上一个static_assert()
来检查args...
的数量是否是偶数;像
static_assert( (sizeof...(Args) & 1U) == 0U, "#Args is odd!");
以下是一个完整的工作示例
#include <map>
#include <tuple>
#include <iostream>
#include <type_traits>
template <typename ... Args, std::size_t ... Is>
std::map<std::string, std::string> getMap
(std::tuple<Args...> const & t, std::index_sequence<Is...> const &)
{ return { {std::get<(Is<<1)>(t), std::get<(Is<<1)+1U>(t)} ... }; }
template <typename... Args>
void something (Args... args)
{
static_assert( (sizeof...(Args) & 1U) == 0U, "#Args is odd!");
auto data = getMap(std::tie(args...),
std::make_index_sequence<(sizeof...(Args)>>1)>{});
for ( auto const & p : data )
std::cout << '[' << p.first << ',' << p.second << ']';
std::cout << std::endl;
}
int main ()
{
something("k1", "v1", "k2", "v2", "k3", "v3"); // compile
//something("k1", "v1", "k2", "v2", "odd!"); // static_assert() failure
}