C++17展开折叠表达式(作为值对)



我正在编写一些代码来实验fold表达式。

我认为我解决这个问题的方法不是最好的方法。

我正试图写一个函数;做某事";具有从扩展参数包获得的值对。第一种类型总是std::string,第二种类型可以变化。

这是我的代码:

#include <memory>
#include <map>
#include <string>
class ThingBase
{
};
template<typename T>
class Thing : public ThingBase
{
public:
Thing(T data)
: data(data)
{
}
T data;
};
std::map<std::string, std::shared_ptr<ThingBase>> the_map;
template<typename T, typename... Args>
static void extract(std::string& name_out, T& value_out,
std::string& name_in, T& value_in,
Args... values)
{
name_out = name_in;
value_out = value_in;
}
template<typename T, typename... Args>
void test_function(Args... args)
{
// expect arguments in pairs like this
std::string name;
T value;
(extract(name, value, args), ...);
the_map.insert(name, std::make_shared(Thing<T>(value)));
}
int main()
{
test_function("argument 1", 1, "argument 2", 2, "argument 3", 3);
test_function("argument 1", 1, "argument 2", 2, "argument 3", "three");
return 0;
}

一些评论:

  • 我不知道如何";展开";参数包,而不编写函数,由于该函数的参数是如何定义的,因此进行扩展。(参见extract函数。(
  • 可能有一种方法可以在没有此功能的情况下实现。这似乎是一件简单的事情,编写一个额外的函数,只需将一些值从一个参数复制到另一个参数,这似乎是个难题,而不是一个合适的解决方案
  • 代码实际上并没有在当前状态下编译。原因如下:
fold_expression_test.cpp:52:68: error: no matching function for call to ‘test_function(const char [11], int, const char [11], int, const char [11], int)’
52 |     test_function("argument 1", 1, "argument 2", 2, "argument 3", 3);
|                                                                    ^
fold_expression_test.cpp:36:6: note: candidate: ‘template<class T, class ... Args> void test_function(Args ...)’
36 | void test_function(Args... args)
|      ^~~~~~~~~~~~~
fold_expression_test.cpp:36:6: note:   template argument deduction/substitution failed:
fold_expression_test.cpp:52:68: note:   couldn’t deduce template parameter ‘T’
52 |     test_function("argument 1", 1, "argument 2", 2, "argument 3", 3);
|                                                                    ^
fold_expression_test.cpp:53:74: error: no matching function for call to ‘test_function(const char [11], int, const char [11], int, const char [11], const char [6])’
53 |     test_function("argument 1", 1, "argument 2", 2, "argument 3", "three");
|                                                                          ^
fold_expression_test.cpp:36:6: note: candidate: ‘template<class T, class ... Args> void test_function(Args ...)’
36 | void test_function(Args... args)
|      ^~~~~~~~~~~~~
fold_expression_test.cpp:36:6: note:   template argument deduction/substitution failed:
fold_expression_test.cpp:53:74: note:   couldn’t deduce template parameter ‘T’
53 |     test_function("argument 1", 1, "argument 2", 2, "argument 3", "three");
|                                                                          ^
  • 使用constexpr-if(if constexpr(语句编写的代码可能会更好/更容易理解。这样做的缺点是编译器会生成大量的函数,每个函数对应一个唯一数量的参数。

  • 如果从代码中看不清楚,那么对参数做什么实际上并不重要。这里的重点是表达使用折叠表达式扩展参数包的意图,其中参数应该成对展开。如果你发现std::map混淆了,你可以忽略所有与之相关的东西。

问题可以是";已解决";通过使用CCD_ 6但是可以使用fold表达式吗?

template<typename T, typename... Args>
void test_function(const std::string& name, const T& value, Args... values)
{
the_map.insert({name, std::make_shared<Thing<T>>(value)});
if constexpr(sizeof...(values) > 0)
{
test_function(values...);
}
}

问题可能是"已解决";通过使用constexpr if。但它能使用fold表达式完成了吗?

您可以将args...打包到tuple中,并通过索引访问相应的键和值,例如

template<std::size_t... Is, typename Tuple>
void test_function_impl(std::index_sequence<Is...>, Tuple t) {
(the_map.insert({
std::get<Is * 2>(std::move(t)), 
std::make_shared<
Thing<std::tuple_element_t<Is * 2 + 1, Tuple>>>(
std::get<Is * 2 + 1>(std::move(t)))}),
...);
}
template<typename... Args>
void test_function(Args... args) {
test_function_impl(
std::make_index_sequence<sizeof...(Args) / 2>{}, 
std::tuple(std::move(args)...));
}

演示

使函数递归并一次提取两个参数可能是一种方法。

#include <string>
#include <iostream>
template<typename T, typename... Rest>
void test_function(const std::string& str, T&& value, Rest&&... rest)
{
std::cout << str << " : " << value << "n";
if constexpr (sizeof...(Rest) > 0) {
test_function(std::forward<Rest>(rest)...);
}
}
int main()
{
test_function("argument 1", 1, "argument 2", 2, "argument 3", 3);
test_function("argument 1", 1, "argument 2", 2, "argument 3", "three");
return 0;
}

在这里,我使用了if constexpr来打破递归,但不带参数的重载也可以工作。

刚刚有了一个基于这个的想法

https://riptutorial.com/cplusplus/example/3208/iterating-over-a-parameter-pack

只是一个初步的想法(未经测试,因为我今晚没有时间测试(-可能是这样的:

struct group
{
std::string;
double;
}
// copied from above link
template <class... Ts>
void print_all(std::ostream& os, Ts const&... args) {
using expander = group[];
(void)expander{0,
(void(os << args), 0)...
};
}

不确定这是否有效-将在本周某个时候尝试测试

最新更新