【发布时间】:2021-07-01 13:58:28
【问题描述】:
我正在编写一个小型信号槽实现,以了解有关模板使用的更多信息。
当我希望编译器从函数的参数中推断出函数模板的模板参数的类型时遇到了问题。
在问这里之前,我观看了几个视频(即 Arthur O'Dwyers 的“模板常规编程”)并阅读了几篇文章,但我就是不知道为什么它不起作用。
我已经构建了一个最小的工作示例:
#include <iostream>
#include <vector>
//Base Event class for passed around events
struct Event
{
virtual ~Event() = default;
Event(int keyCode) : keyCode_{keyCode} {}
int keyCode_;
};
//Event to be passed around
struct KeyPressedEvent final : Event
{
~KeyPressedEvent() { std::cout << "KeyPressedEvent dtor " << std::endl; }
KeyPressedEvent(int keyCode) : Event(keyCode) { std::cout << "KeyPressedEvent param. ctor " << std::endl; }
};
//Class which holds a callback(slot)
struct Window
{
Window(int y) { std::cout << "window param ctor\n"; }
void kpEventHandler(KeyPressedEvent& kpEvent)
{
std::cout << "non-static member staticKpEventHandler() : " << kpEvent.keyCode_ << "\n";
}
};
//Signal which holds connections to callbacks
template<typename Signature> struct Signal;
template<typename Ret, typename ArgType>
struct Signal<Ret(ArgType)>
{
struct Connection
{
using Instance_t = void*;
using Call_t = void(*)(void*, ArgType&&);
//template <typename Type>
Connection(void* instance, Call_t call) :
instance_{instance}, call_{std::move(call)} {}
bool operator==(const Connection& other) const noexcept
{
bool cmpInstance = instance_ == other.instance_;
bool cmpCall = call_ == other.call_;
return cmpInstance && cmpCall;
}
Instance_t instance_;
Call_t call_;
};
std::vector<Connection> connections_;
template<typename C, Ret(C::* func)(ArgType)>
static void call(void* instancePtr, ArgType&& arg)
{
C* instance = static_cast<C*>(instancePtr);
if (instance != nullptr)
{
(instance->*func)(std::forward<ArgType>(arg));
}
}
template<typename C, Ret(C::* func)(ArgType)>
void connect(C& instance)
{
connections_.emplace_back(&instance, std::move(&call<C, func>));
}
template<typename C, Ret(C::*func)(ArgType)>
void disconnect(C& instance)
{
Connection conn{&instance, &call<C, func>};
connections_.erase(std::remove(connections_.begin(), connections_.end(), conn), connections_.end());
}
};
//Test code
int main()
{
{
Window window{5};
Signal<void(KeyPressedEvent&)> signal;
signal.connect<&Window::kpEventHandler>(window); //Error C2974 'Signal<void (KeyPressedEvent &)>::connect': invalid template argument for 'C', type expected
signal.disconnect<&Window::kpEventHandler>(window); //Error C2974 'Signal<void (KeyPressedEvent &)>::disconnect': invalid template argument for 'C', type expected
}
std::cin.get();
return 0;
}
当我将测试代码更改为以下时,它显然可以工作。
signal.connect<Window, &Window::kpEventHandler>(window);
signal.disconnect<Window, &Window::kpEventHandler>(window);
当我使用函数指针的auto 模板参数将Signal::connect() 和Signal::disconnect() 更改为以下实现时,我让它工作了。
template<auto func, typename C>
void connect(C& instance)
{
static_assert(std::is_member_function_pointer<decltype(func)>::value,
"Signal::connect: func is not a pointer to a member function");
connections_.emplace_back(&instance, std::move(&call<C, func>));
}
template<auto func, typename C>
void disconnect(C& instance)
{
static_assert(std::is_member_function_pointer<decltype(func)>::value,
"Signal::disconnect: func is not a pointer to a member function");
Connection conn{&instance, &call<C, func>};
connections_.erase(std::remove(connections_.begin(), connections_.end(), conn), connections_.end());
}
signal.connect<&Window::kpEventHandler>(window); //works fine
signal.disconnect<&Window::kpEventHandler>(window); //works fine
当我在此解决方案中更改模板参数的顺序时,我也会收到错误消息:
template<typename C, auto func>
void disconnect(C& instance)
{
static_assert(std::is_member_function_pointer<decltype(func)>::value,
"Signal::disconnect: func is not a pointer to a member function");
Connection conn{&instance, &call<C, func>};
connections_.erase(std::remove(connections_.begin(), connections_.end(), conn), connections_.end());
}
signal.disconnect<&Window::kpEventHandler>(window); //Error C2974 'Signal<void (KeyPressedEvent &)>::disconnect': invalid template argument for 'C', type expected
所以我的问题是:
- 为什么我的原版编译器不能推断出参数的类型?
- 为什么当我将
Signal::connect()和Signal::disconnect()实现更改为使用“auto func”时它会起作用? - 为什么我在“
auto func”解决方案中更改模板参数的顺序时会出现编译器错误?
谢谢你帮助我!
【问题讨论】:
标签: c++ templates c++17 template-argument-deduction type-deduction