9

私は単純なベクトル クラスを作成しており、特定の長さのベクトルでのみ使用できるメンバー関数をいくつか用意したいと考えています (たとえば、3 要素ベクトルの外積)。私は std::enable_if に出くわしました。それは私がやりたいことができるように見えますが、正しく動作させることができないようです。

#include <iostream>
#include <type_traits>

template<typename T, unsigned int L>
class Vector
{
    private:
        T data[L];

    public:
        Vector<T,L>(void)
        {
            for(unsigned int i = 0; i < L; i++)
            {
                data[i] = 0;
            }
        }

        T operator()(const unsigned int i) const
        {
            return data[i];
        }

        T& operator()(const unsigned int i)
        {
            return data[i];
        }

        Vector<typename std::enable_if<L==3, T>::type, L> cross(const Vector<T,L>& vec2) const
        {
            Vector<T,L> result;

            result(0) = (*this)(1) * vec2(2) - (*this)(2) * vec2(1);
            result(1) = (*this)(2) * vec2(0) - (*this)(0) * vec2(2);
            result(2) = (*this)(0) * vec2(1) - (*this)(1) * vec2(0);

            return result;
        }
}; 

int main(void)
{
    Vector<double,3> v1;
    Vector<double,3> v2;
    Vector<double,3> v3;
    //Vector<double,4> v4;

    v1(0) = 1;
    v1(1) = 2;
    v1(2) = 3;

    v2(0) = 4;
    v2(1) = 5;
    v2(2) = 6;

    v3 = v1.cross(v2);

    std::cout << v3(0) << std::endl;
    std::cout << v3(1) << std::endl;
    std::cout << v3(2) << std::endl;

    return 0;
}

上記のコードは正しくコンパイルおよび実行されますが、の宣言のコメントを外すと、Vector<double,4> v4コンパイル時に次のエラーが発生します。

vec.cpp: In instantiation of ‘class Vector<double, 4u>’:
vec.cpp:46:22:   required from here
vec.cpp:29:59: error: no type named ‘type’ in ‘struct std::enable_if<false, double>’

誰かが私が間違っている場所を指摘できますか?

4

1 に答える 1

9
 template<unsigned LL = L>
  Vector<typename std::enable_if<LL==3 && L == 3, T>::type, LL>
  cross(const Vector<T,LL>& vec2) const
  {
    Vector<T,L> result;

    result(0) = (*this)(1) * vec2(2) - (*this)(2) * vec2(1);
    result(1) = (*this)(2) * vec2(0) - (*this)(0) * vec2(2);
    result(2) = (*this)(0) * vec2(1) - (*this)(1) * vec2(0);

    return result;
  }

PS。なぜこれがこのように機能するのですか?

変数の定義によりv4、クラステンプレートの暗黙的なインスタンス化が発生しますVector。これにより、クラスメンバー関数の宣言の暗黙的なインスタンス化が発生します(14.7.1暗黙的なインスタンス化[temp.inst]#1)。もちろん、この後者のインスタンス化はエラーになります。

代わりに、同じ句に従ってメンバー関数をメンバーテンプレートに変更すると、この時点でメンバーテンプレート自体がインスタンス化され、このインスタンス化は多かれ少なかれ次のようになります。

template<unsigned LL = 3>
Vector<typename std::enable_if<LL==3 && 3 == 3, double>::type, LL>
cross(const Vector<double,LL>& vec2) const;

これは完全に有効なテンプレート宣言です。この時点では、これ以上のインスタンス化は実行しません(実行できません)。

ただし、実際に呼び出しようとするとcross、これは間違いなく「メンバー/関数定義が存在する必要があるコンテキスト」であるため、(14.7.1暗黙のインスタンス化[temp.inst]#2、#3)によると、crossテンプレート(2番目のcrossテンプレート、外部クラステンプレートのインスタンス化の結果であるテンプレート)は暗黙的にインスタンス化され、std::enable_ifその作業を行う機会が与えられます。ちなみに、これはSFINAEの原則が適用される状況です。

PPS。OPの質問とは直接関係ありませんが、もう少し詳しく説明しますが、同様の状況を処理するために、メンバーをテンプレートとして宣言する必要は必ずしもないことを言及する価値があります。

クラステンプレートのメンバーが特定のインスタンス化に対して「有効」ではない場合でも、クラステンプレートをインスタンス化できる場合があります。たとえば、次のようになります。

#include <type_traits>

template<typename T>
struct S
{
  T x;

  T foo () const { return x; }

  typename std::remove_pointer<T>::type bar () const { return *x; }
};

S<int> x;
S<int *> y;

どうやら、インスタンス化では、のタイプがであるため、S<int>*xは無効xですint。ただし、このプログラムは正しいです。重要な点は、暗黙的なインスタンス化では、メンバーの宣言のみがインスタンス化されるということです。上記の場合、インスタンス化S<int>によって宣言 int bar() const;がインスタンス化されます。これは完全に正しい宣言です。

もちろん、後で次のようにの定義をインスタンス化しようとするとS<int>::bar、次のようになります。

void f()
{
  x.foo ();
  //  x.bar (); // error
  y.foo ();
  y.bar ();
}

エラーが発生します。

(これは、C ++標準の上記の2つの段落に引き続き続きます)

于 2012-12-09T10:20:37.093 に答える