1

C++ を C++98 (私は C 出身です) として使用するのではなく、C++11 を使用しようとしています。問題を解く。

通常、継承を使用してタイプに基づいてメソッドを追加し、ユーザーに依存しますが、特性を使用したいのですが、エンドユーザーが「カスタムのもの」を使用することを期待していません。これは実験だからです.

次のように、True と False の型を作成することから始めました。

struct True {
    static const bool value = true;
};

struct False {
    static const bool value = false;
};

次に、enable_if構造体またはクラスが構造体 (C の意味ではサイズなど) と名前空間の両方であるという事実を使用することを理解している私の定義は、次のようになります。

template<bool B,class T>
struct EnableIf {};

template<class T>
struct EnableIf<true,T> {
    typedef T type;
};

テンプレートTが何であれ、「タイプ」と呼ばれる「名前空間タイプのメンバー」(可能であれば)を持つことだけEnableIfを期待しています。trueこれはうまくいくようです。

false/default のケースは、アクセスしようとするとEnableIf<false,T>::typetype

これまでのところすべてが正しいと確信していますが、冗長になっているだけです。

私のテストケース

私はテストの場としてリストを選択しました (ここでも調査中なので、標準セットは使用しません) 通常、これを行うにはクラス階層を使用しますlist。余分なメンバーはint find(T*);、T* が T の ID であるためです。

次に、これを and に拡張し、int find(T&); int find(T&,int)==int count(T&)を使用して T を比較します。これは、ユーザーに任せることで意味することです。ユーザーは、タイプについて知っていることに基づいて、必要なリストを選択できます。

EnableIf代わりにこれを行うために(後でstd::enable_if自信を持って)使用したいのですが、テンプレートがスタンプアウトされたenable場合、型がそのように使用できる場合にのみ機能が使用されます。

リスト定義

template<class T>
class List {
public:
    typedef typename T::hasEquality hasEquality;
    virtual ~List() {}
    virtual T& operator[](int index) =0;
    virtual const T& operator[](int index) const =0;
    virtual void append(T*) =0;
    virtual int length() const =0;
    virtual
        typename EnableIf<hasEquality::value, bool>::type
    operator==(const List<T>& rhs) const {
        if(length() == rhs.length()) {
            for(int k=0;k!=length();k++) {
                if(!((*this)[k] == rhs[k])) {
                    return false;
                }
            }
            return true;
        } else {
            return false;
        }
    }
    virtual
        typename EnableIf<T::hasEquality::value, int>::type
        count(const T& what) const =0;
};

セットではなくリストなので、順序が重要です。hasEqualityこれは、次の意味で推移的であるべきであることがわかります。

もし T が等価の概念を持っているなら、T のリストも等価の概念を持っている

次に、単方向リストの実装に進みます。

試験の種類

class A {
public:
    A(int value) { val = value; }
    typedef True hasEquality;
    bool operator==(const A& rhs) const {
        if(val == rhs.val) {
            return true;
        }
        return false;
    }

private:
    int val;
};

class B {
public:
    typedef False hasEquality;
};

結果

int main(int,char**) {
    LinkedList<A> listA;
    listA.append(new A(6));
    A a(6);
    std::cout<<"there are "<<listA.count(a)<<" of them\n";
    return 0;
}

ご想像のとおり、これは機能します。私の最初のテストには最初に B が含まれていましたが、それが問題を引き起こします。

int main(int,char**) {
    LinkedList<A> listA;
    listA.append(new A(6));
    A a(6);
    std::cout<<"there are "<<listA.count(a)<<" of them\n";

    LinkedList<B> listB;

    return 0;
}

これはそうではなく、次のように失敗します:

src/main.cpp: In instantiation of ‘class List<B>’:
src/main.cpp:77:7:   required from ‘class LinkedList<B>’
src/main.cpp:176:16:   required from here
src/main.cpp:59:2: error: no type named ‘type’ in ‘struct EnableIf<false, bool>’
  operator==(const List<T>& rhs) const {
  ^
src/main.cpp:73:3: error: no type named ‘type’ in ‘struct EnableIf<false, int>’
   count(const T& what) const =0;
   ^
src/main.cpp: In instantiation of ‘class LinkedList<B>’:
src/main.cpp:176:16:   required from here
src/main.cpp:134:3: error: no type named ‘type’ in ‘struct EnableIf<false, int>’
   count(const T& what) const {
   ^
make: *** [build/main.o] Error 1

何らかの理由で typename 行の後にエラー マーカーを配置しますEnableIffalse

これがなぜなのかはよくわかりませんが、そうです、ありませんtypeが、これは設計によるものです!

リサーチ

http://en.cppreference.com/w/cpp/types/enable_if

それを引用するには:

template<bool B, class T = void>
struct enable_if {};

template<class T>
struct enable_if<true, T> { typedef T type; };

私のものは名前だけが異なり、デフォルトの T は無効です。これを私のものに追加しても(予想どおり)、問題は解決しません。

http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/SFINAE

http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/enable-if

私の考えを確認します。

ボーナス質問

constexpr と static

最初に私は試しstruct False { constexpr bool operator==(bool what) { return !what; } };てみましたが、同じですTrue

しかし、これはうまくいきませんでした。「静的」という言葉を使用して operator== を修飾することはできconstexpr static bool is(bool what);ませんが、同じ効果を得るために呼び出されたメソッドを使用できたのに、なぜ constexpr は静的を意味しないのでしょうか?

私の考えでは、constexprs は実際には存在せず、設計は仮想の反対のようなものです。インスタンスを使用して静的メソッドを呼び出すことができないと言うものは何もありません。C++03 標準を確認したところです。セクション 9.4 でこれが確認されています。 、これは変わりましたか?

スフィナエ

SFINAE を使用して、定義されていないFalse場合を想定することは可能でしょうか? hasMemberこれは基本的なタイプなどでは機能しないことを理解しています。これは実験です。自信が持てるまでは、これらの手法を本番環境で使用することはありません。

4

2 に答える 2

3

問題はここにあります:

virtual
    typename EnableIf<T::hasEquality::value, int>::type
    count(const T& what) const =0;

ジェネリック プログラミング (テンプレート) とオブジェクト指向プログラミング スタイルが競合する別の例を見つけました。

SFINAE は、テンプレートで機能するメタプログラミング手法です。見た目 ( の使用T) にもかかわらず、上記で宣言された関数はテンプレートではありません。これは、テンプレート クラス内の通常の関数です。テンプレート タイプ パラメータTは のパラメータであり、のパラメータではListありませんcount

たとえば、次は SFINAE の例です。

template<class T>
class List {
public:
template<class T>
class List {
public:
    // ...
    template <typename U>
    typename EnableIf<std::is_same<U, T>::value && U::hasEquality::value, int>::type
    count(const U& what) const { std::cout << "1\n"; }

    template <typename U>
    typename EnableIf<std::is_same<U, T>::value && !U::hasEquality::value, int>::type
        count(const U& what) const { std::cout << "2\n"; }
};

};

int main() {
    A a(1);
    B b;
    List<A> la;
    la.count(a); // outputs 1
    List<B> lb;
    lb.count(b); // ouputs 2
}

2 つの がテンプレートになっていることに注意してcountください ( でパラメータ化されていUます)。Tが と同じタイプの場合にのみ、両方ともアクティブになりUます。これは、受け入れるTだけの回避策です (完全ではありません。たとえば、暗黙的な変換を破棄します)。1 つ目はさらにそれU::hasEquality::value == trueを必要とし、2 つ目はその逆を必要とします。

ここでの重要なポイントは、SFINAE がテンプレートで機能することです。

しかし、ご覧のとおり、私はあなたのデザインを変更し、count非仮想にしました。残念ながら、countテンプレート関数は仮想化できないため、上記の関数を仮想化することはできません。

基本的な問題は次のとおりです。テンプレート関数は、呼び出されたときにのみインスタンス化されます。そのため、コンパイラーが (私のバージョン) を解析するとき、そのすべてのインスタンス化が存在することをListまだ知りません。count

仮想関数ごとに、仮想テーブルにエントリが存在する必要があり、コンパイラが解析Listするときに、仮想テーブルにエントリがいくつあるかを知る必要があります。

したがって、Listコンパイラは解析時にテンプレート インスタンスの数を認識していませんが、仮想関数の数を認識している必要があります。結論は、テンプレート関数を仮想にすることはできないということです。

于 2013-10-03T07:49:49.160 に答える