【问题标题】:variadic templates: invalid use of void expression可变参数模板:无效表达式的无效使用
【发布时间】:2013-02-18 04:46:12
【问题描述】:

我正在尝试为事件创建一个通用集合,以便它可以重复用于不同类型的事件集。在使用可变参数模板时,我遇到了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

给我一​​个“错误:无效使用无效表达式”。 谁能告诉我,如何解决这个问题?

【问题讨论】:

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


    【解决方案1】:

    问题是您的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 的调用都按照明确定义的顺序发生。

    【讨论】:

    • 我相信他宁愿有(update_map(curr_map, ts),ts)...
    【解决方案2】:

    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
    

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

    现在,这也完全消除了对 update_map 函数的需求:

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

    太棒了。

    【讨论】:

    • 不错的提示,但我并不真正需要排序(但我需要不同的输入类型或更准确地说:不同的 event_types - 也许我应该使用 map_update 的返回来检查)
    • 我认为这太过分了。要强制执行明确定义的顺序,您可以使用数组初始化技巧,如我的回答所示。 :-)
    • @Nawaz 除非每次你这样做,你都必须在评论中说“我这样做是为了强制对任务进行按顺序评估”。 do_in_order 函数既说明了它的作用,又说明了它的作用。 forget 解决方案使用int forget[]= 样板和(X,0) 样板,总共11 个非文档样板字符(+6 表示forget),而do_in_order 使用8 个非文档样板字符(+11 表示do_in_order)。它的重量更轻,一旦do_in_order 被写入一次! :) 我挑战你找到一个运行速度较慢的情况。
    • @Yakk:int do_in_order[] = {(update_map(curr_map, ts),0)...}; 更好吗?
    • @Nawaz 至少它是正确的,并且对于零大小的事件集合不会失败。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-08-18
    • 1970-01-01
    • 2019-03-10
    • 2014-03-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多