模板类和别名导致编译错误

时间:2016-05-29 11:56:31

标签: c++ templates alias

我一直在使用wxWidgets开发一个应用程序(细节,但不是特定于问题)。我有一个名为MyPanel的类,在其中,我有一个计时器,我根据需要启动和停止。我将计时器用于许多不同的目的,尽管一次只能激活其中一个目的。为此,我有两种方法: StartTimer和StopTimer,当计时器启动或停止时,两者都必须通过计时器处理程序绑定/取消绑定。为了简化代码,我使用了别名作为处理程序类型。这是宣言:

using TimerHandler = void (MyPanel::*) (wxTimerEvent&);

class MyBasePanel : public wxGLCanvas
{
   ...
    std::unique_ptr<ShaderProgram> m_program;
    ...
}

class MyPanel : public MyBasePanel
{
    ...
    void StartTimer(TimerHandler handler);
    ...
}

因此,所有内容都会编译并运行。现在问题。我希望从MyBasePanel派生出多个类,每个类使用不同的ShaderProgram类和不同的处理程序。所以,我改变了我的类声明如下:

template <typename T>
class MyBasePanel : public wxGLCanvas
{
    ...
    std::unique_ptr<T> m_program;
    ...
}

template <typename T>
class MyPanel : public MyBasePanel<T>
{
    ...
}

现在,当我尝试编译时,Visual Studio会在别名上显示此错误:

  

&#39; MyPanel&#39;:使用类模板需要模板参数列表

好的,所以我将别名更改为:

template <typename T>
using TimerHandler = void (MyPanel<T>::*) (wxTimerEvent&)

和StartTimer(和StopTimer)方法声明:

void StartTimer(TimerHandler<T> handler);

现在Visual Studio吐出:

  

&#39; MyPanel :: StartTimer&#39;:无法将函数定义与现有声明匹配

     

注意:请参阅&#39; MyPanel :: StartTimer&#39;

的声明      

注意:定义

     

&#39; void MyPanel:StartTimer(void(__ cdecl MyPanel :: *)(wxTimerEvent&amp;))&#39;

     

现有声明

     

&#39; void MyPanel:StartTimer(void(__ cdecl MyPanel :: *)(wxTimerEvent&amp;))&#39;

请注意,定义和声明是相同的。一些调查表明,C ++标准并不真正允许以这种方式组合别名和模板。

我想出了一个我认为可行的问题的潜在解决方案。不是将处理程序传递给StartTimer和StopTimer方法,而是传入一个标志,然后测试标志并绑定/取消绑定匹配的处理程序。我没有试过这个,但我认为它会起作用。我担心的是,这看起来很笨拙,非常像C;应该有一个更像C ++的解决方案。

如何将以上内容更改为&#34; work&#34;?

1 个答案:

答案 0 :(得分:0)

我正在发布这个解决方案,因为它适用于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;
}