2

次のセグメントで is_class を使用しようとしています (より大きなセグメントから取り除かれています) が、うまくいかないようです。どうしたの?

#include <type_traits>

template<typename U, typename = void>
struct xxxU_Impl
{
    static void xxxU_push (const U& value);
};

template<typename U> void xxxU_push (const U& value) { xxxU_Impl<U>::xxxU_push (value); }

template<typename U>
struct xxxU_Impl<U *, typename std::enable_if<std::is_class<U>::value>::type>
{
    static void xxxU_push (const U *& value) { }
};

class Foo
{
public:
    int mFoo;
};

int main () {
    Foo * pFoo = new Foo;

    xxxU_push<Foo *>(pFoo);
}

これは、gcc -std=c++11 test.cpp コマンド ラインを使用した cygwin 上の gcc v4.7.2 です。

出力は次のとおりです。

test.cpp: In instantiation of 'void xxxU_push(const U&) [with U = Foo*]':
test.cpp:26:23:   required from here
test.cpp:9:63: error: no matching function for call to 'xxxU_Impl<Foo*, void>::xxxU_push(Foo* const&)'
test.cpp:9:63: note: candidate is:
test.cpp:14:17: note: static void xxxU_Impl<U*, typename std::enable_if<std::is_class<_Tp>::value>::type>::xxxU_push(const U*&) [with U = Foo]
test.cpp:14:17: note:   no known conversion for argument 1 from 'Foo* const' to 'const Foo*&'

**

アップデート

:** これは、タイプが同一であることを私見が示す注釈付きの変更されたコードです。それでも、コンパイルエラーが発生します。

#include <type_traits>

template<typename U, typename = void>
struct xxxU_Impl
{
    static void xxxU_push (const U  & value);   // U=Foo*:  const Foo* & value ==
                                                //          Foo const * & value
};

template<typename U> void xxxU_push (const U  & value)  // U=Foo*:  const Foo* & value ==
                                                        //          Foo const * & value
{ xxxU_Impl<U>::xxxU_push (value); }

template<typename U>
struct xxxU_Impl<U *, typename std::enable_if<std::is_class<U>::value>::type>
{
    static void xxxU_push (U const * & value) { }   // U=Foo:   Foo const * & value
};

class Foo
{
public:
    int mFoo;
};

int main () {
    Foo* pFoo = new Foo;

    xxxU_push<Foo*>(pFoo);
}

どうしたの?

Thx、D

PS is_enum を使用した同様のスキームは、ヒッチなしで機能します。

4

1 に答える 1

8

特性はstd::is_class<>正常に機能しており、コンパイラは問題が何であるかをほとんど伝えています。

test.cpp:14:17: 注: 引数 1 からFoo* constへの既知の変換はありませんconst Foo*&

この方法で関数テンプレートを呼び出しています。

xxxU_push<Foo *>(pFoo);

つまり、UになりますFoo*。関数のシグネチャは次のとおりです。

template<typename U>
void xxxU_push (const U& value)

これと同等です:

template<typename U>
void xxxU_push (U const& value)

Foo*そして、あなたのために置き換えた後、Uこれを得る:

void xxxU_push (Foo* const& value)

したがって、valueは へのポインタへの定数参照Fooです。関数内で、クラス テンプレートを次のようにインスタンス化します。

xxxU_Impl<U>::xxxU_push (value);

つまり、再び代入Foo*すると:U

xxxU_Impl<Foo*>::xxxU_push (value);

これで、クラス テンプレートの特殊化は次のように定義されます。

template<typename U>
struct xxxU_Impl<U *, typename std::enable_if<std::is_class<U>::value>::type>
{
    static void xxxU_push (const U *& value) { }
};

Foo*テンプレート引数としてインスタンス化する場合、クラス型であるbeUと推定されます。したがって、クラス テンプレートは失敗することなくインスタンス化されます (これがあなたが望むものかどうかはわかりませんが、これは間違いなく起こることです)。特に、関数は次のようにインスタンス化されます。FooxxxU_push()

 static void xxxU_push (const Foo *& value) { }

これはこれと同等です:

 static void xxxU_push (Foo const*& value) { }

違いがわかりますか?呼び出し元のサイトでは、非定数ポインターへの定数参照があります。ここでは、定数ポインターへの非定数参照があります! これら 2 つの型は異なり、コンパイラは引数を変換できないと文句を言います。

たとえば、xxxU_push()次のように署名を変更することで、エラーを修正できます。

static void xxxU_push (U * const& value) { }
//                     ^^^^^^^^^^

この変更後、ここでコンパイル全体を見ることができます。

アップデート:

staticコメントからのフォローアップとして、メンバー関数xxxU_push()が へのポインターを受け入れる必要があることがわかりconstます。

 static void xxxU_push (Foo const*& value) { }

この場合、決定を下す必要があります。ポインターへFoo*の非定数参照を受け入れる関数に a を渡すことはできません。constただし、参照を削除することはできます。

 static void xxxU_push (Foo const* value) { }

これは、プログラムをコンパイルするための最初の可能性です。2 番目の可能性は、次へのポインターを提供するように呼び出しサイトを変更することconstです。

int main () {
    Foo * pFoo = new Foo;
    Foo const* pConstFoo = pFoo;
    xxxU_Impl<Foo*>::xxxU_push(pConstFoo);
//                             ^^^^^^^^^
}
于 2013-03-10T12:30:42.977 に答える