可变参数模板:无效使用void表达式

时间:2013-02-17 19:03:28

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

我正在尝试为事件创建一个通用集合,以便它可以重用于不同类型的事件集。在使用可变参数模板的同时,我遇到了THIS answer,这帮助了我的例子:

#include <boost/test/unit_test.hpp>

#include <string>
#include <unordered_map>

namespace
{
struct Event3 {
    static const int event_type = 3;
    int a;
};

struct Event5 {
    static const int event_type = 5;
    double d;
};

struct Event7 {
    static const int event_type = 7;
    std::string s;
};


template <class ...K>
void gun(K...) {}

template <class... Ts>
class EventCollection
{
    template <typename T>
    void update_map(std::unordered_map<int, size_t> & map, const T &)
    {
        BOOST_CHECK(map.find(T::event_type) == map.end());
        map[T::event_type] = sizeof(T);
    }


public:
    std::unordered_map<int, size_t> curr_map;

    EventCollection(Ts... ts)
    {
        gun(update_map(curr_map, ts)...); // will expand for each input type
    }
};

} // namespace

BOOST_AUTO_TEST_CASE( test_01 )
{
    Event3 x{13};
    Event5 y{17.0};
    Event7 z{"23"};

    EventCollection<Event3, Event5, Event7> hoshi(x, y, z);
    BOOST_CHECK_EQUAL(hoshi.curr_map.size(), 3);
}

然而,行

gun(update_map(curr_map, ts)...); // will expand for each input type

给我一​​个'错误:无效使用void表达式'。 任何人都能告诉我,如何解决这个问题?

2 个答案:

答案 0 :(得分:4)

问题是您的update_map会返回void。因此你不能这样写:

gun(update_map(curr_map, ts)...); 

因为update_map的返回值应该作为参数传递给gun

修复是将某些内容传递给gun作为参数,因此您可以执行此操作:

gun( (update_map(curr_map, ts),0)...); 

现在表达(update_map(curr_map, ts),0)原来是0,它作为参数传递给gun。这应该工作。您可以将其视为:

T argmument = (update_map(curr_map, ts),0);  //argument is 0, and T is int

-

另外,正如另一个答案所指出的那样,对gun()的参数的评估顺序是未指定的(意味着调用函数update_map的顺序,未指定),这可能导致不希望的结果。另一种解决方案已经解决了这个问题。这是另一个(这有点棘手和容易!):

//ensure that the size of the below array is at least one.
int do_in_order[] = {0, (update_map(curr_map, ts),0)...};

因为数组元素的初始化顺序是明确定义的(从左到右),所以现在对update_map的所有调用都按照明确的顺序进行。

答案 1 :(得分:0)

update_map是一个返回void的函数。

该行包括调用update_map,然后将返回值传递给gun

您无法将void返回值传递给其他函数。

因此“无效使用void表达式”。

有很多方法可以解决此问题,包括让update_map返回struct empty {};

请注意,您的代码会导致update_map的调用以未指定的顺序发生。这很容易导致意外行为。

我可以建议:

void do_in_order();
template<typename F0, typename... Functors>
void do_in_order( F0&& f0, Functors&& funcs... ) {
  f0();
  do_in_order( std::forward<Functors>(funcs)... );
}

然后将调用替换为gun

do_in_order([&]{update_map(curr_map, ts);}...); // will expand for each input type

将要做的事情打包成lambdas,然后调用它们以便传递它们。

现在,这也完全取消了对update_map功能的需求:

do_in_order([&]{
  BOOST_CHECK(curr_map.find(ts::event_type) == curr_map.end());
  map[ts::event_type] = sizeof(ts);
}...);

太棒了。