模板 类 和别名导致编译错误
Templating Classes and Aliases Causes Compile Errors
我一直在使用wxWidgets开发一个应用程序(细节,但不是具体问题)。我有一个名为 MyPanel 的 class,其中有一个计时器,可以根据需要启动和停止。我将计时器用于许多不同的目的,尽管一次只能激活其中一个目的。为此,我有两种方法:
StartTimer 和 StopTimer,两者都必须在定时器启动或停止时将定时器处理程序传递给 bind/unbind。为了简化代码,我为处理程序类型使用了一个别名。这是声明:
using TimerHandler = void (MyPanel::*) (wxTimerEvent&);
class MyBasePanel : public wxGLCanvas
{
...
std::unique_ptr<ShaderProgram> m_program;
...
}
class MyPanel : public MyBasePanel
{
...
void StartTimer(TimerHandler handler);
...
}
因此,一切都可以编译和运行。现在的问题。我想要多个 class 派生自 MyBasePanel,每个使用不同的 ShaderProgram class 和不同的处理程序。所以,我改变了我的 class 声明如下:
template <typename T>
class MyBasePanel : public wxGLCanvas
{
...
std::unique_ptr<T> m_program;
...
}
template <typename T>
class MyPanel : public MyBasePanel<T>
{
...
}
现在,当我尝试编译时,Visual Studio 在别名上给出了这个错误:
'MyPanel': use of class template requires template argument list
好的,所以我将别名更改为:
template <typename T>
using TimerHandler = void (MyPanel<T>::*) (wxTimerEvent&)
StartTimer(和 StopTimer)方法声明为:
void StartTimer(TimerHandler<T> handler);
现在Visual Studio吐槽一下:
'MyPanel::StartTimer': unable to match function definition to an existing declaration
note: see declaration of 'MyPanel::StartTimer'
note: definition
'void MyPanel:StartTimer(void (__cdecl MyPanel::* )(wxTimerEvent &))'
existing declarations
'void MyPanel:StartTimer(void (__cdecl MyPanel::* )(wxTimerEvent &))'
注意定义和声明是一样的。一些调查表明 C++ 标准实际上不允许以这种方式组合别名和模板。
对于这个问题,我想出了一个我认为可行的潜在解决方案。不是将处理程序传递给 StartTimer 和 StopTimer 方法,而是传递一个标志,然后测试该标志和 bind/unbind 匹配的处理程序。我没有尝试过这个,但我认为它会起作用。我担心的是这看起来很笨拙而且很像 C;应该有一个更像 C++ 的解决方案。
如何将上面的内容更改为 "work"?
我将继续发布此解决方案,因为它适用于 clang 和 g++。看来你用的是visual-studio,所以,我不知道这个解决方案是否适合你。
#include <iostream>
template <typename U>
using Timer = void (U::*)(); // This is your timer.
// A small template meta-program to make sure that your
// Timer<T> type actually points to some callable object.
// It is needed to make sure that the type T in 'Timer<T>'
// is indeed Mypanel or similar class and that has a function named
// cb (can be changed as well)
namespace meta_detail {
template <typename T>
constexpr auto has_cb(const T& obj, int) -> decltype(std::declval<T&>().cb(), bool())
{
return true;
}
template <typename T>
constexpr auto has_cb(const T& obj, long) -> decltype(std::declval<T&>().cb(), bool())
{
return false;
}
}
template <typename T>
class Test {
public:
template <typename U>
void take_it(Timer<U> t) {
// This is where the meta-program helps you. Compile time
// Checking.
static_assert(meta_detail::has_cb(U(), 0), "Nopes!");
(this->*t)();
}
void cb() {
std::cout << "Callback" << std::endl;
}
};
int main() {
Test<int> tt;
Timer<Test<int>> timer = &Test<int>::cb;
tt.take_it(timer);
return 0;
}
我一直在使用wxWidgets开发一个应用程序(细节,但不是具体问题)。我有一个名为 MyPanel 的 class,其中有一个计时器,可以根据需要启动和停止。我将计时器用于许多不同的目的,尽管一次只能激活其中一个目的。为此,我有两种方法: StartTimer 和 StopTimer,两者都必须在定时器启动或停止时将定时器处理程序传递给 bind/unbind。为了简化代码,我为处理程序类型使用了一个别名。这是声明:
using TimerHandler = void (MyPanel::*) (wxTimerEvent&);
class MyBasePanel : public wxGLCanvas
{
...
std::unique_ptr<ShaderProgram> m_program;
...
}
class MyPanel : public MyBasePanel
{
...
void StartTimer(TimerHandler handler);
...
}
因此,一切都可以编译和运行。现在的问题。我想要多个 class 派生自 MyBasePanel,每个使用不同的 ShaderProgram class 和不同的处理程序。所以,我改变了我的 class 声明如下:
template <typename T>
class MyBasePanel : public wxGLCanvas
{
...
std::unique_ptr<T> m_program;
...
}
template <typename T>
class MyPanel : public MyBasePanel<T>
{
...
}
现在,当我尝试编译时,Visual Studio 在别名上给出了这个错误:
'MyPanel': use of class template requires template argument list
好的,所以我将别名更改为:
template <typename T>
using TimerHandler = void (MyPanel<T>::*) (wxTimerEvent&)
StartTimer(和 StopTimer)方法声明为:
void StartTimer(TimerHandler<T> handler);
现在Visual Studio吐槽一下:
'MyPanel::StartTimer': unable to match function definition to an existing declaration
note: see declaration of 'MyPanel::StartTimer'
note: definition
'void MyPanel:StartTimer(void (__cdecl MyPanel::* )(wxTimerEvent &))'
existing declarations
'void MyPanel:StartTimer(void (__cdecl MyPanel::* )(wxTimerEvent &))'
注意定义和声明是一样的。一些调查表明 C++ 标准实际上不允许以这种方式组合别名和模板。
对于这个问题,我想出了一个我认为可行的潜在解决方案。不是将处理程序传递给 StartTimer 和 StopTimer 方法,而是传递一个标志,然后测试该标志和 bind/unbind 匹配的处理程序。我没有尝试过这个,但我认为它会起作用。我担心的是这看起来很笨拙而且很像 C;应该有一个更像 C++ 的解决方案。
如何将上面的内容更改为 "work"?
我将继续发布此解决方案,因为它适用于 clang 和 g++。看来你用的是visual-studio,所以,我不知道这个解决方案是否适合你。
#include <iostream>
template <typename U>
using Timer = void (U::*)(); // This is your timer.
// A small template meta-program to make sure that your
// Timer<T> type actually points to some callable object.
// It is needed to make sure that the type T in 'Timer<T>'
// is indeed Mypanel or similar class and that has a function named
// cb (can be changed as well)
namespace meta_detail {
template <typename T>
constexpr auto has_cb(const T& obj, int) -> decltype(std::declval<T&>().cb(), bool())
{
return true;
}
template <typename T>
constexpr auto has_cb(const T& obj, long) -> decltype(std::declval<T&>().cb(), bool())
{
return false;
}
}
template <typename T>
class Test {
public:
template <typename U>
void take_it(Timer<U> t) {
// This is where the meta-program helps you. Compile time
// Checking.
static_assert(meta_detail::has_cb(U(), 0), "Nopes!");
(this->*t)();
}
void cb() {
std::cout << "Callback" << std::endl;
}
};
int main() {
Test<int> tt;
Timer<Test<int>> timer = &Test<int>::cb;
tt.take_it(timer);
return 0;
}