如何实现一个可变参数模板,该模板将用户输入读入所有提供的变量?

时间:2017-10-12 06:23:28

标签: c++ c++11 templates variadic-templates

我目前正在尝试自学变量模板。但是,通过简单的添加模板,我无法理解任何内容。

目前我想要一个能够执行以下操作的模板:

  1. 采用多种类型
  2. 采用要求用户以下列格式输入的参数:

    T值,字符串描述符

  3. 然后逐个浏览每个变量,在读取变量之前打印描述符
  4. 例如,输出应如下所示:

    x (int) //this is the descriptor
    //here something is being read into the variable x
    y (int) //this is another descriptor
    //something else is being read into y
    .
    .
    .
    

    由于它始终是相同的操作,这应该是可能的。然而,我最好的尝试看起来像这样

    template<typename t,typename... Args>
    void generic_reader(t first,string desc,Args... args)
    {
        cout<<desc<<endl;
        cin>>first;
        generic_reader(args);
    }
    

    显然这不起作用。但是,我无法想到另一种方法。我再次开始使用可变参数模板。

    有人可以给我一个详细解释的解决方案吗?

4 个答案:

答案 0 :(得分:4)

这是使用递归的一种方法。

#include <iostream>

// provide a terminating case 
void generic_read()
{
}

// provide the general case which picks off the first 2 arguments
// and forwards the rest to another version of itself.

template<typename T, typename Printable, typename...Rest>
void generic_read(T& value ,Printable&& desc,Rest&&...rest)
{
    std::cout << desc << std::endl;
    std::cin >> value;
    generic_read(std::forward<Rest>(rest)...);
}

// test
int main()
{
    int x;
    double y;

    generic_read(x, "an integer:", y, "a double");
}

答案 1 :(得分:2)

你基本上就在那里 - 你只是错过了一个基本案例。此外,您在...的递归调用中错过了generic_reader;它应该是generic_reader(args...)

以下是一些可以执行您要执行的操作的代码:

#include <string>
#include <iostream>

void generic_reader()
{
    std::cout << "no more stuff!" << std::endl;
}

template <typename T, typename... Args>
void generic_reader(T& first, const std::string& desc, Args&... args)
{
    std::cout << desc << std::endl;
    std::cin >> first;
    std::cin.ignore(100, '\n');
    generic_reader(args...);
}


int main()
{
    int x, y, z;

    generic_reader(x, "x", y, "y", z, "z");

    std::cout << "x: " << x << " y: " << y << " z: " << z << std::endl;

    return 0;
}
`

遍历代码:您的方法是正确的,但是当您用完参数时没有基本情况。在倒数第二个调用中,剩余的参数为(z, "z"),可成功替换模板。但在那之后,最后一次调用generic_reader(),没有剩余的参数。您需要提供一个可以接受最终(空)参数列表的候选者。

最后一点 - 你会注意到我通过引用传递了first,所以我可以写入原始变量。如果这样做,请确保通过引用传递剩余的Args...!否则,递归调用将按值传递剩余的args,并在第一个调用后不再引用原始变量。

答案 2 :(得分:1)

It seems to me that you're using a sequence of std::pairs where the first type is fixed, std::string, and the second one is a variable type.

So you can write your function as

template <typename ... Args>
void generic_reader (std::pair<std::string, Args> & ... ps)
 { /* do something */}

and call it as

auto a = std::make_pair<std::string>("a", short(0));
auto b = std::make_pair<std::string>("b", 1);
auto c = std::make_pair<std::string>("c", 2L);
auto d = std::make_pair<std::string>("d", 3LL);

generic_reader(a, b, c, d);

Unfortunately I don't know (before c++17) how to use ps... in the body of the function so, in C++11 and in C++17, the best I can think is a solution based on recursion (as your original, with the recursion call corrected in generic_reader(args...);)

Starting from C++17 it's available a new (and more powerful) mode of use variadic arguments (look for "fold expression") and your function ca be simply written as

template <typename ... Args>
void generic_reader (std::pair<std::string, Args> & ... ps)
 { ( (std::cout << ps.first << std::endl, std::cin >> ps.second), ... ) ; }

The following is a full working C++17 example

#include <utility>
#include <iostream>

template <typename ... Args>
void generic_reader (std::pair<std::string, Args> & ... ps)
 { ( (std::cout << ps.first << std::endl, std::cin >> ps.second), ... ) ; }

template <typename ... Args>
void variadic_printer (Args & ... as)
 { ( (std::cout << as.first << ", " << as.second << std::endl), ... ) ; }

int main ()
 { 
   auto a = std::make_pair<std::string>("a", short(0));
   auto b = std::make_pair<std::string>("b", 1);
   auto c = std::make_pair<std::string>("c", 2L);
   auto d = std::make_pair<std::string>("d", 3LL);

   generic_reader(a, b, c, d);

   variadic_printer(a, b, c, d);
 }

答案 3 :(得分:1)

如果你不想使用递归,你总是可以使用它(c ++ 14,但是对于c ++ 11存在index_sequence的实现):

#include <utility>
#include <iostream>
#include <tuple>

template <class Tuple, std::size_t... Is>
void generic_reader_impl(std::index_sequence<Is...>, Tuple&& tuple) {
    std::size_t dummy[] = { 0ul, 
        (static_cast<void>(std::cout << std::get<2ul*Is + 1ul>(tuple) << std::endl),
         static_cast<void>(std::cin >> std::get<2ul*Is>(tuple)),
         Is)... 
    };
    static_cast<void>(dummy);
}

template <class... Args>
void generic_reader(Args&&... args) {
    generic_reader_impl(std::make_index_sequence<sizeof...(Args) / 2>{}, std::forward_as_tuple(std::forward<Args>(args)...));
}

int main() {
    int x;
    double y;
    generic_reader(x, "an integer:", y, "a double");
    std::cout << x << std::endl;
    std::cout << y << std::endl;
}

输出:

1
1.2

[live demo]