简体   繁体   English

C ++ Variadic参数,第一个参数

[英]C++ Variadic parameters with out first one

it's any possibility to use a variadic parameters with out specification of first one? 是否可以使用变量参数而不是第一个参数?

For example: 例如:

This code is perfectly fine: 这段代码非常好:

void something(const char* layoutAndButton, ...)
{
    va_list ap;
    va_start(ap, layoutAndButton);
    std::map<std::string, std::string> data;
    while (*layoutAndButton != '\0') {
        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);
}

But I would like to have the something function in this way: 但是我希望以这种方式获得某些功能:

void something(const char*...)

It's any possibility to do something like that? 做这样的事情有可能吗? and after that to access the members? 然后访问会员? if yes, how? 如果有,怎么样?

Thanks 谢谢

Here is how you would use a C++ variadic template. 以下是使用C ++可变参数模板的方法。 You can call something() with any even number of const char* arguments eg something("k1", "v1", "k2", "v2") . 你可以使用任何偶数个const char*参数调用something() ,例如something("k1", "v1", "k2", "v2") It will build a map<string,string> from these arguments by recursively calling the buildMap() function, and then call useMap() to do whatever actual work you want done with the map. 它将通过递归调用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);
}

As state in comment std::initializer_list seems to do the job 正如评论中的状态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;
    }
}

or even, if you really need a map: 甚至,如果你真的需要一张地图:

void something(const std::map<std::string, std::string>& layoutAndButtons)
    for (auto const& p : layoutAndButtons) {
        std::cout << p.first << ':' << p.second << std::endl;
    }
}

With usage similar to: 使用类似于:

something({{"Some", "things"}, {"are", "done"}});

If you really want variadic template, I suggest: 如果你真的想要可变参数模板,我建议:

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

If you can use C++14 ( std::index_sequence and std::make_index_sequence ) you can avoid recursion wrapping your args... in a std::tuple , generating a list of indexes and initializing the std::map with indexes and std::get() . 如果你可以使用C ++ 14( std::index_sequencestd::make_index_sequence ),你可以避免在std::tuple递归包装你的args... ,生成一个索引列表并用索引初始化std::mapstd::get()

I mean: if you write an helper function as follows 我的意思是:如果你写一个帮助函数如下

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

in something() you can initialize data as follows something()您可以按如下方式初始化data

auto data = getMap(std::tie(args...),
                   std::make_index_sequence<(sizeof...(Args)>>1)>{});

but I also suggest to precede this line with a static_assert() to check that the number of args... is even; 但是我也建议在这行之前加一个static_assert()来检查args...的数量是否均匀; something like 就像是

static_assert( (sizeof...(Args) & 1U) == 0U, "#Args is odd!"); 

The following is a full working example 以下是一个完整的工作示例

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM