【问题标题】:Function that accepts variadic template list of brace constructed objects接受大括号构造对象的可变参数模板列表的函数
【发布时间】:2014-10-13 19:39:46
【问题描述】:

模板元编程专家您好。

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

最好是这样的:

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

还有:

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

然后类/函数应该能够通过调用在编译时推导出来的转换函数来转换这些参数,因此它在内部会以字符串作为键和值的 std::map 结束。

原因只是语法糖。在我的用例中,如果提供了不支持的类型,调用者不必担心转换参数并获得编译时错误,这将非常方便。

如果调用者明确使用 make_pair,我已经让它工作了:

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

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

我已经尝试创建自己的 std::pair 类,具有针对各种值类型的特化,但如果我使用大括号初始化程序调用,我的编译器 (gcc) 无法找到它,就像使用标准 std 的情况一样::地图。即使参数具有不同的类型,我的编译器似乎也将其视为 std::initializer_list。

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

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

非常感谢!

编辑

示例代码:

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>)’

【问题讨论】:

  • C++14 肯定会减少很多冗长,例如你可以只写这个std::make_pair("Time"s, 42ms) 而不是你写的。好多了。
  • 你有没有尝试实现一些东西?
  • @Nawaz:这实际上是一个好点。另外,你的例子让我觉得除了简单类型之外的任何东西的转换可能都不是一个很好的设计。不应由被调用者决定如何转换,例如。字符串的时间戳。我仍然觉得知道是否可以这样做很有趣。
  • 没有模板 void foo(Ts... a){ bar({rest...}); } 工作?

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


【解决方案1】:

我猜是下面的代码:

template <typename... Ts>
void foo(Ts... ts) {}

foo({1,2}, {3,4});

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

template <typename T>
void foo(T t) {}

foo({1,2});

大括号括起来的列表根本没有类型,因此无法推导出来。

但是,知道 大括号封闭的初始化列表 可用于通过 非显式 构造函数初始化具体类型,并且编译器能够推断出 @987654325 @ 类型为 std::initializer_list&lt;T&gt;,以下代码使用 老式 可变参数列表按预期工作:

#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

【讨论】:

  • 这非常酷,非常接近我所期待的,但是 foo() 需要用固定数量的参数声明吗?好像有点不灵活。我尝试使用可变参数模板参数 bun 遇到了完全相同的问题。
  • @kingguru 是的,这就是为什么我称它为 old-fashioned,您可以声明任意数量的默认为 nullptr 的此类参数,因为它们是默认情况下,您不必使用那么多参数(请参阅我在调用该函数时使用三个参数而不是四个参数)
  • @kingguru 和DEMO 你可以看到我检查了参数是否默认
【解决方案2】:

我能想到的一个解决方案是使用可变参数模板:

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
}

【讨论】:

  • 谢谢,但是我认为有一个单独的参数列表,其中键/值对没有被分开但被评估为“丑陋”并且绝对容易出错,这就是我寻找的原因一种使用大括号构造函数的方法。
猜你喜欢
  • 1970-01-01
  • 2014-04-21
  • 1970-01-01
  • 2018-05-18
  • 2016-09-03
  • 2016-09-02
  • 2017-02-01
  • 2015-05-06
  • 2016-01-02
相关资源
最近更新 更多