1. ホーム
  2. c++

[解決済み] std::enable_if によるメンバ関数の条件付きコンパイル

2022-04-21 06:49:36

質問

の使い方を理解するために、簡単なサンプルを動作させようとしています。 std::enable_if . を読んだ後、私は この回答 簡単な例を作るのはそれほど難しくはないだろうと思いました。私が使いたいのは std::enable_if を使用して、2つのメンバー関数のどちらかを選択し、どちらか一方だけを使用できるようにします。

残念ながら、以下のものはgcc 4.7でコンパイルできず、何時間も試した後、私の間違いは何なのか皆さんにお聞きしています。

#include <utility>
#include <iostream>

template< class T >
class Y {

    public:
        template < typename = typename std::enable_if< true >::type >
        T foo() {
            return 10;
        }
        template < typename = typename std::enable_if< false >::type >
        T foo() {
            return 10;
        }

};


int main() {
    Y< double > y;

    std::cout << y.foo() << std::endl;
}

gccは以下の問題を報告します。

% LANG=C make CXXFLAGS="-std=c++0x" enable_if
g++ -std=c++0x    enable_if.cpp   -o enable_if
enable_if.cpp:12:65: error: `type' in `struct std::enable_if<false>' does not name a type
enable_if.cpp:13:15: error: `template<class T> template<class> T Y::foo()' cannot be overloaded
enable_if.cpp:9:15: error: with `template<class T> template<class> T Y::foo()'

なぜg++は2番目のメンバ関数の間違ったインスタンス化を削除しないのでしょうか?規格によると std::enable_if< bool, T = void >::type は、ブーリアン・テンプレート・パラメータが真であるときのみ存在します。しかし、なぜg++はこれをSFINAEと見なさないのでしょうか?オーバーロードのエラーメッセージは、g++が2番目のメンバ関数を削除せず、これがオーバーロードであるべきだと考えている問題からきているのではと思います。

どうすればいいですか?

SFINAEは、テンプレート引数の引数控除における置換が構成体を不正な形にする場合にのみ機能します。そのような代入はありません。

私もそれを思い、試しに std::is_same< T, int >::value! std::is_same< T, int >::value となり、同じ結果になります。

それは、クラス・テンプレートがインスタンス化されたとき(型のオブジェクトを作成したときに起こる Y<int> このとき、すべてのメンバー宣言がインスタンス化されます(必ずしもその定義やボディがインスタンス化されるわけではありません!)。その中には、メンバー・テンプレートも含まれます。なお T は既知であり !std::is_same< T, int >::value は偽をもたらします。そのため、これはクラス Y<int> を含む。

class Y<int> {
    public:
        /* instantiated from
        template < typename = typename std::enable_if< 
          std::is_same< T, int >::value >::type >
        T foo() {
            return 10;
        }
        */

        template < typename = typename std::enable_if< true >::type >
        int foo();

        /* instantiated from

        template < typename = typename std::enable_if< 
          ! std::is_same< T, int >::value >::type >
        T foo() {
            return 10;
        }
        */

        template < typename = typename std::enable_if< false >::type >
        int foo();
};

std::enable_if<false>::type は存在しない型にアクセスするので、その宣言は不正な形式です。従って、あなたのプログラムは無効です。

メンバーテンプレートの enable_if は、メンバーテンプレート自身のパラメータに依存します。そうすると、型全体が依存したままなので、宣言は有効です。そのうちの1つを呼び出そうとすると、そのテンプレート引数の引数控除が起こり、SFINAEが期待通りに起こります。参照 この質問 と対応する回答があります。