Top/article/Partial-specialization-of-member-methods
English | Japanese
English | Japanese

Menu

  • Top
  • Akihiko Yamaguchi 山口 明彦
  • Project プロジェクト
  • Text テキスト
  • Recent articles 最近の記事
  • Articles 記事一覧 (a to z)
  • Search 検索

Tags タグ †

  • [c++][bash][python][latex][php]
  • [linux][windows][mac][android]
  • [math][algorithm][idea][trick]
  • [liboctave][opencv][git][ros]
  • [setting][bug][general]
↑

Recent articles 最近の記事 †

2019-07-02
  • article/Display-Unix-Time
  • article/Synchronize-Linux-Time-to-Remote
2018-09-27
  • article/python-multimode-singleton
2018-09-02
  • article/rosinstall-git-default-remote
2017-07-28
  • article/SubMenu
2017-03-05
  • article/Import-a-different-version-of-OpenCV-in-Python
2015-08-17
  • article/DRC-finals-2015
2015-01-05
  • article/Upgrade-Android-to-Lollipop
2015-01-01
  • article/Kernel-panic-of-Linux-when-using-Xtion
  • article/Do-not-skip-freeing-data-when-using-tri-mesh-in-ODE
Access: 2/1983 - Admin
These search terms have been highlighted:[c++]

Partial specialization of member methods

メンバ関数の部分特殊化がしたい

[c++]
2009-08-27

C++ でメンバ関数を部分特殊化しようとすると(例えばテンプレートクラスのテンプレートメンバ関数を,ある特定の型について特殊化する),コンパイルエラーとなる.gcc なら "error: invalid use of incomplete type" だとか, "error: enclosing class templates are not explicitly specialized" などのエラーが発生する.しかし,現実には,部分特殊化したいことも多い.そこで,やや強引な解決策を実装してみた.

問題提起 †

template <typename T1, typename T2>
struct TTest
{
  T1 ex()
    {
      ...
    }
};
template <>
void TTest<void, void>::ex()  // OK
{
  ...
}

これは,メンバ関数 TTest::ex を T1=void, T2=void について(完全に)特殊化している例で,ちゃんとコンパイルできる.これに対し,

template <typename T1, typename T2>
struct TTest
{
  T1 ex()
    {
      ...
    }
};
template <typename T2>
void TTest<void, T2>::ex()  // NG
{
  ...
}

このように,メンバ関数 TTest::ex を T1=void のみについて部分特殊化すると(i.e. T2 についてはテンプレートのまま),コンパイルエラーとなる. gcc の場合, "error: invalid use of incomplete type 'struct TTest<void, T2>'" というエラーだ.

↑

解決 †

クラス TTest 内にテンプレート関数オブジェクトを作る.部分特殊化したい関数の代わりに,このテンプレート関数オブジェクトを特殊化しておき,部分特殊化したい関数からは,このテンプレート関数オブジェクトを呼び出す.ただし,テンプレート関数オブジェクトの完全な特殊化を C++ は許していないから,ダミーテンプレート引数を設けることで対処する.以上をまとめると,

template <typename T1, typename T2>
struct TTest
{
  T1 ex()
    {
      // 特殊化したい関数からは,関数オブジェクト dummy_ex を呼び出す
      return dummy_ex<T1>()();
    }

  // 通常の型 T1 に対する実装:
  template <typename TX, typename dummy_type=void>
  struct dummy_ex
    {
      TX operator()()
        {
          ...
        }
    };
  // dummy_ex を void について特殊化し, T1==void に対して実装する:
  template <typename TX>
  struct dummy_ex <void, TX>
    {
      void operator()()
        {
          ...
        }
    };
};

ダミーテンプレート引数 dummy_type を持たせない場合, dummy_ex の特殊化は完全な特殊化になってしまい,"error: explicit specialization in non-namespace scope 'struct TTest<T1, T2>'" などというエラーを吐く.

クラス外部で実装する場合は,

template <typename T1, typename T2>
struct TTest
{
  T1 ex()
    {
      return dummy_ex<T1>()();
    }

  template <typename TX, typename dummy_type=void>
  struct dummy_ex;
};

// 通常の型 T1 に対する実装:
template <typename T1, typename T2>
template <typename TX, typename dummy_type>
struct TTest<T1,T2>::dummy_ex
{
  TX operator()()
    {
      ...
    }
};

// dummy_ex を void について特殊化し, T1==void に対して実装する:
template <typename T1, typename T2>
template <typename TX>
struct TTest<T1,T2>::dummy_ex< void, TX >
{
  void operator()()
    {
      ...
    }
};

のように書けばよい.この場合も, dummy_type を除去すると,"error: enclosing class templates are not explicitly specialized" というエラーが発生し,コンパイルできない.

↑

少し脱線: 参照型,ポインタ型への特殊化 †

dummy_ex を TX& とか TX* に特殊化する場合,部分特殊化となるため dummy_type は不要となる.例えばポインタ型への特殊化は,

template <typename T1, typename T2>
struct TTest
{
  T1 ex()
    {
      return dummy_ex<T1>()();
    }

  template <typename TX>
  struct dummy_ex
    {
      TX operator()()
        {
          ...
        }
    };
  // ポインタ型に対する部分特殊化
  template <typename TX>
  struct dummy_ex<TX*>
    {
      TX* operator()()
        {
          ...
        }
    };
};

のような感じになる.

↑

サンプル †

簡単なサンプルコードをひとつ.

#include <iostream>
using namespace std;

template <typename T1, typename T2>
struct TTest
{
  T1 ex()
    {
      return dummy_ex<T1>()();
    }

  template <typename TX, typename dummy_type=void>
  struct dummy_ex
    {
      TX operator()()
        {
          TX tmp(1);
          cout<<"default member function"<<endl;
          return tmp;
        }
    };
  template <typename TX>
  struct dummy_ex< void, TX >
    {
      void operator()()
        {
          cout<<"member function specialized to void"<<endl;
          return;
        }
    };
};

int main(int argc, char**argv)
{
  TTest<int,int> test1;
  TTest<void,int> test2;
  test1.ex();
  test2.ex();
  return 0;
}

g++ に "-ansi -pedantic -Wall" (厳格なANSI準拠モード)などをつけてコンパイルした.

↑

関連サイト †

  • Explicit specialization in non-namespace scope : c++
  • enclosing class templates are not explicitly specialized? - CodeGuru Forums
  • 突然クラスのテンプレートメンバー関数の特殊化ができるか気になった
  • 続)メンバー関数テンプレートの特殊化

コンパイラによっては,エラーのでかたにかなり違いがあるらしい.間違い・コンパイラ依存性などを教えて頂けると助かります.




Last-modified:2014-12-31 (Wed) 03:11:58 (3834d)
Site admin: Akihiko Yamaguchi.
Written by: Akihiko Yamaguchi.
System: PukiWiki 1.5.0. PHP 5.2.17. HTML conversion time: 0.012 sec.