вариативные шаблоны: недопустимое использование выражения void

Я пытаюсь создать общую коллекцию для событий, чтобы ее можно было повторно использовать для различных наборов событий. Играя с вариативными шаблонами, я наткнулся на ЭТОТ ответ, который помог мне в моем примере:

#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». Может ли кто-нибудь сказать мне, как это решить?


person user2081073    schedule 17.02.2013    source источник


Ответы (2)


Проблема в том, что ваш 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 происходят в четко определенном порядке.

person Nawaz    schedule 17.02.2013
comment
Я считаю, что он предпочел бы (update_map(curr_map, ts),ts)... - person K-ballo; 17.02.2013

update_map — это функция, возвращающая void.

Эта строка состоит из вызова update_map и передачи возвращаемого значения в gun.

Вы не можете передать возвращаемое значение void другой функции.

Следовательно, «недопустимое использование выражения void».

Есть много способов исправить это, включая update_map return 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

который упаковывает действия в лямбда-выражения, которые затем вызываются для их передачи.

Теперь это также полностью устраняет необходимость в функции update_map:

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

что потрясающе.

person Yakk - Adam Nevraumont    schedule 17.02.2013
comment
хороший совет, но мне действительно не нужен порядок (но мне нужны разные типы ввода или, точнее: разные типы событий - может быть, я должен использовать возврат map_update, чтобы проверить это) - person user2081073; 17.02.2013
comment
Я думаю, что это перебор. Чтобы обеспечить четко определенный порядок, вы можете использовать трюк с инициализацией массива, как показано в моем ответе. :-) - person Nawaz; 17.02.2013
comment
@Nawaz, за исключением того, что каждый раз, когда вы это делаете, вы должны говорить, что я делаю это, чтобы обеспечить упорядоченную оценку задач в комментарии. Функция do_in_order говорит, что она делает, и затем делает это. Решение forget использует шаблоны int forget[]= и шаблоны (X,0), всего 11 недокументированных шаблонных символов (+6 для forget), а do_in_order использует 8 недокументированных шаблонных символов (+11 для do_in_order). Это легче, как только do_in_order было написано один раз! :) И я призываю вас найти ситуацию, когда он будет работать медленнее. - person Yakk - Adam Nevraumont; 17.02.2013
comment
@Yakk: int do_in_order[] = {(update_map(curr_map, ts),0)...}; лучше? - person Nawaz; 17.02.2013
comment
@Nawaz, по крайней мере, это правильно и не ошибается для коллекций событий нулевого размера. - person Johannes Schaub - litb; 17.02.2013
comment
@JohannesSchaub-litb: Хорошо. Вот : int do_in_order[] = {0, (update_map(curr_map, ts),0)...};. Фиксированный? - person Nawaz; 17.02.2013
comment
@Nawaz Слишком тяжелый: вы только что добавили больше шаблонов! Кроме того, все еще хрупкий - если update_map возвращает тип, который перегружает operator,, у вас больше нет гарантии упорядочения. Итак, вам нужно еще 6 символов шаблона, чтобы решить эту проблему. И даже без этого решение просто хакерское: создание массива только для использования гарантий упорядочения? Если бы не было другого решения, я бы пошел на это. Между тем, взгляните на новейшее использование do_in_order выше — я удалил update_map, и решение do_in_order теперь занимает меньше символов, чем неупорядоченный sltn! - person Yakk - Adam Nevraumont; 18.02.2013