#c #templates
Вопрос:
У меня есть занятие TriggerEvent
:
template <typename ReturnType, typename... ArgumentTypes>
class TriggerEvent<ReturnType(ArgumentTypes...)>
{
public:
using EventCallback = std::function<ReturnType(ArgumentTypes...)>;
using InvokeCallback = std::function<bool(const ReturnTypeamp;)>;
void Subscribe(EventCallbackamp;amp; callback);
void Clear();
void Invoke(ArgumentTypesamp;amp;... args);
void Invoke(ArgumentTypesamp;amp;... args, InvokeCallbackamp;amp; invoke_callback);
private:
std::mutex access_mtx_;
std::list<EventCallback> event_callbacks_;
};
Я не могу скомпилировать объект этого класса с шаблоном TriggerEvent<void(T)
>, потому void
что тип > не может использоваться для InvokeCallback
псевдонима и в Invoke
методе с обратным вызовом в его реализации.
Псевдоним и метод для ReturnType
этого отличаются от void
. Как я могу удалить псевдоним и метод из класса, когда ReturnType
он есть void
?
Комментарии:
1. Специализация по данному
void
делу?2. Дело не только в том, чтобы удалить псевдоним. Вам также необходимо убедиться, что остальная часть кода действительно работает. Какой метод лучше всего, будет зависеть от остальной части вашего кода. Специализации SFINAE и шаблонов-это два ваших основных варианта.
3. @Someprogrammerdude, вы имеете в виду создание триггерного события второго класса со специализацией void? Я знаю об этом, но я пытаюсь найти эффективный способ, потому что в этом случае мне нужно скопировать и вставить много кода.
Ответ №1:
Вы можете использовать вспомогательный шаблон для создания соответствующего типа обратного вызова invoke:
bool(T)
bool()
когдаT
==void
Вот так:
template <typename ReturnType>
struct invoke_callback_ref {
using type = bool(const ReturnTypeamp;);
};
template <>
struct invoke_callback_ref<void> {
using type = bool();
};
template <typename ReturnType, typename... ArgumentTypes>
class TriggerEvent {
public:
using EventCallback = std::function<ReturnType(ArgumentTypes...)>;
using InvokeCallback = std::function<typename invoke_callback_ref<ReturnType>::type>;
. . .
Однако помните, что std::function
это связано с накладными расходами во время выполнения.
Я бы использовал std::function
только тогда, когда это неизбежно — например, при хранении обратных вызовов событий в списке. Во всех других местах используйте вызываемый тип напрямую. Это устраняет проблему работы со ссылками на void
, и вы можете просто использовать if constexpr
их для вызова void
обратного вызова.
Например:
template <typename ReturnType, typename... ArgumentTypes>
class TriggerEvent {
public:
using EventCallback = std::function<ReturnType(ArgumentTypes...)>;
template<typename T>
void Subscribe(Tamp;amp; callback) {
event_callbacks_.emplace_back(std::forward<T>(callback));
}
void Invoke(ArgumentTypesamp;amp;... args) {
for (autoamp; cb : event_callbacks_) cb(args...);
}
template<typename T>
void Invoke(ArgumentTypesamp;amp;... args, Tamp;amp; invoke_callback) {
for (autoamp; cb : event_callbacks_) {
if constexpr (std::is_same_v<ReturnType, void>) {
cb(args...);
invoke_callback();
} else {
invoke_callback(cb(args...));
}
}
}
private:
std::list<EventCallback> event_callbacks_;
};