c++ - クラスとエイリアスをテンプレート化するとコンパイルエラーが発生する

原文 c++ templates alias

私はwxWidgetsを使用してアプリケーションを開発しています(詳細ですが、問題に固有ではありません)。 MyPanelというクラスがあり、その中に、必要に応じて開始および停止するタイマーがあります。タイマーはさまざまな目的で使用しますが、一度にアクティブにできる目的は1つだけです。これを行うには、2つの方法があります。
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がエイリアスに次のエラーを表示します。


'MyPanel':クラステンプレートを使用するには、テンプレート引数リストが必要です


OK、それでエイリアスを次のように変更しました:

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


StartTimer(およびStopTimer)メソッドの宣言により、

void StartTimer(TimerHandler<T> handler);


これでVisual Studioは次のように出力します。


'MyPanel::StartTimer':関数定義を既存の宣言に一致させることができません

注:「MyPanel::StartTimer」の宣言を参照してください

注:定義

'void MyPanel:StartTimer(void(__cdecl MyPanel::*)(wxTimerEvent&))'

既存の宣言

'void MyPanel:StartTimer(void(__cdecl MyPanel::*)(wxTimerEvent&))'


定義と宣言は同じであることに注意してください。一部の調査では、C++標準では、このようにエイリアスとテンプレートを組み合わせることが実際には許可されていないことが示されています。

私はこの問題の解決策の1つを考え出しました。ハンドラーをStartTimerおよびStopTimerメソッドに渡す代わりに、フラグを渡してから、フラグをテストし、一致するハンドラーをバインド/バインド解除します。私はこれを試していませんが、うまくいくと思います。私の懸念は、これは無骨で非常にCのように見えることです。もっとC++のような解決策があるはずです。

上記のものを「仕事」に変更するにはどうすればよいですか?
答え
このソリューションは、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;
}
関連記事

c++ - オブジェクトリストの類似のアイテムを削除する

c++ - テンプレート<char *、char *>オブジェクトのc++ソートベクトル

c++ - 異なるシグネチャを持つ関数を持つ派生クラスの基本クラスを設計

c++ - cornerHarrisを使用してコーナーの数を知る

c++ - とにかくmallocを呼び出さないようにできますか

c++ - Linuxターミナルを介して別のプロセスのstdinにデータを送信する

c++ - QMYSQLドライバーが読み込まれていないQt 5.6 Linux Ubuntu 16.04

c++ - MFC CToolBar更新メカニズムのバグ?

c++ - SSDを使用する場合、20スレッドではなく1つのスレッドを使用して20の異なるファイルを書き込む方が良いですか?

c++ - C++ダブルから16進数のコンソール出力は解決に役立つ必要があります