函数,该函数接受大括号构造对象的可变模板列表

Function that accepts variadic template list of brace constructed objects

本文关键字:函数 对象 列表      更新时间:2023-10-16

你好,模板元编程专家。

我正在尝试编写一个(类成员)函数,理想情况下可以将其作为一个参数,称为类型不可知映射。

理想情况下类似于:

foo({"Bar", 42}, {"Baz", "Blargh");

以及:

foo({"Blargh", "Bleh"}, {"Biz", 43.6}, {"Bam", {43, 43, 43}});

然后,类/函数应该能够通过调用在编译时推导的转换函数来转换这些参数,因此它在内部最终会得到一个以字符串为键和值的std::映射。

原因仅仅是句法上的糖。在我的用例中,如果提供了不受支持的类型,那么调用方就不必担心转换参数和编译时出错,这将非常方便。

如果调用方显式使用make_pair,如中所示,我已经使其工作

foo(std::make_pair<std::string, std::chrono::miliseconds>("Time", 42));

但这自然不是很干净,也绝对不会比调用方自己将值类型转换为std::string更方便。

我曾尝试创建自己的std::pair类,并对各种值类型进行专门化,但如果我使用大括号初始值设定项进行调用,就像使用标准std::map一样,我的编译器(gcc)无法找到它。我的编译器似乎将其视为std::initializer_list,即使参数具有不同的类型。

我或多或少得出的结论是,即使在C++14标准中,我所尝试的也是不可能的,但我并不完全确定。

有没有人对如何解决这个问题有任何想法,或者能够解释为什么如果是这样的话,这是不可能的?

非常感谢!

编辑

示例代码:

template<typename Value, typename... Args>
void foo(const std::pair<std::string, Value>& val, Args... args)
{
  foo(args...);
}
void foo(const std::pair<std::string, int>& val) {}
void foo(){}

调用foo()如下:

foo({"key", 42});

不扩展模板并且有效,而:

foo({"key", 42}, {"another", 53})

编译失败,错误为:

no matching function for call to ‘foo(<brace-enclosed initializer list>, <brace-enclosed initializer list>)’

我猜下面的代码:

template <typename... Ts>
void foo(Ts... ts) {}
foo({1,2}, {3,4});

由于与完全相同的原因而无法编译

template <typename T>
void foo(T t) {}
foo({1,2});

大括号封闭列表根本没有类型,因此无法推导。

然而,知道大括号包围的初始值设定项列表可以通过非显式构造函数来初始化具体类型,并且编译器能够推导出std::initializer_list<T>T类型,使用老式可变列表的以下代码可以按预期工作:

#include <initializer_list>
struct AgnosticMap
{
    AgnosticMap(std::nullptr_t) {}
    template <typename T, typename U>
    AgnosticMap(T t, U u) {}
    template <typename T, typename U>
    AgnosticMap(T t, std::initializer_list<U> il) {}
};
void foo(AgnosticMap a1
       , AgnosticMap a2 = nullptr
       , AgnosticMap a3 = nullptr
       , AgnosticMap a4 = nullptr)
{
}
int main()
{
    foo({"Blargh", "Bleh"}, {"Biz", 43.6}, {"Bam", {43, 43, 43}});
}

DEMO

我可以想象的一个解决方案是使用可变模板:

template<typename T, typename U, typename... Rest>
auto foo(T t, U u, Rest... rest){
    //deal with t and u,
    //and call foo(rest...) recursievely
}