13

特定のクラスMyClassまたは派生クラスMyClassDerを使用して呼び出すと、特定のアクションを実行するためにいくつかのテンプレート関数をオーバーロードしようとしています。コードは次のとおりです。

#include <iostream>

struct MyClass {
    virtual void debug () const {
        std::cerr << "MyClass" << std::endl;
    };
};

struct MyClassDer : public MyClass {
    virtual void debug () const {
        std::cerr << "MyClassDer" << std::endl;
    };
};

template <typename T> void  func  (const T& t) {
    std::cerr << "func template" << std::endl;
}

void func (const MyClass& myClass) {
    std::cerr << "func overloaded" << std::endl;
    myClass.debug ();
}


int main(int argc, char **argv) {
    func (1);
    MyClass myClass;
    func (myClass);
    MyClassDer myClassDer;
    func (myClassDer);
}

出力は次のとおりです。

func template
func overloaded
MyClass
func template

func (myClassDer)の代わりにテンプレート関数を呼び出しますvoid func (const MyClass& myClass)。期待される動作を得るにはどうすればよいですか?

ありがとう

4

8 に答える 8

4

コードが機能しなかった理由については、@David の優れた説明を参照してください。それを機能させるには、非表示のテンプレート パラメーターを追加して SFINAE ("Substition Failure is not an Errro) を使用できますRequires(名前はドキュメント化のみを目的としています)。

template <
     typename T, typename Requires = typename 
     std::enable_if<!std::is_base_of<MyClass, T>::value, void>::type 
> 
void  func  (const T& t) {
    std::cerr << "func template" << std::endl;
}

Tこれにより、が と等しいか派生した場合は常にこのテンプレートのオーバーロード解決が無効MyClassになり、代わりに通常の関数が選択されます (完全一致のみを考慮するテンプレート引数推定とは対照的に、派生からベースへの変換が実行されます)。明らかにこれをいじって、内部に重複しない条件でいくつかのオーバーロードを追加std::enable_ifして、考慮される関数オーバーロードをきめ細かく選択することができます。ただし、注意してください。SFINAE は微妙です。

ライブの例

: 関数テンプレートの既定のテンプレート パラメーターを使用して、C++11 構文で SFINAE を作成しました。C++98 では、通常のデフォルト パラメータを追加するか、戻り値の型を変更する必要があります。

于 2013-09-24T13:06:30.110 に答える
3

SFINAE を使用できます。

#include <type_traits>

template <typename T>
void func (const T& t, typename std::enable_if<!std::is_base_of<MyClass, T>::value>::type * = nullptr) {
    std::cout << "func template" << std::endl;
}

template <
    typename T
    , typename = typename std::enable_if<std::is_base_of<MyClass, T>::value>::type
>
void func (const T& t) {
    std::cout << "func overloaded" << std::endl;
    t.debug ();
}

C++11 がない場合、boost は同じ機能を提供します。

実際の例

編集

これは C++11 なしで動作するはずです (boost を使用):

#include "boost/type_traits.hpp"

template <typename T>
void func (const T& t, typename boost::enable_if<!boost::is_base_of<MyClass, T>::value>::type * = 0) {
    std::cout << "func template" << std::endl;
}

template <typename T>
void func (const T& t, typename boost::enable_if<boost::is_base_of<MyClass, T>::value>::type * = 0) {
    std::cout << "func overloaded" << std::endl;
    t.debug ();
}
于 2013-09-24T13:07:12.860 に答える
1

ポリモーフィズムは実行時に発生しますが、オーバーロードされた関数の選択はコンパイル時に発生します。

したがって、コンパイル時に受け入れるのに最適なオーバーロードは次のとおりMyClassDerです。

func<MyClassDer> (const MyClassDer& t)

それよりも

func<MyClass> (const MyClass& t)

次に、コンパイラは最初のものを選択します。


問題を解決する可能性は次のとおりです。

func(static_cast<MyClass&>(myClassDer));
于 2013-09-24T13:07:56.770 に答える
0
MyClass *myClassDer = new MyClassDer;
func(*myClassDer);
delete myClassDer;
于 2013-09-24T12:56:46.860 に答える
0

基本型にキャストするだけです:

MyClassDer myClassDer;
func(static_cast<MyClass&>(myClassDer));
于 2013-09-24T13:13:37.657 に答える
0

オーバーロードされた関数のシグネチャは、

void func (const MyClass& myClass)
{
    std::cerr << "func overloaded" << std::endl;
    myClass.debug ();
}

つまりMyClass、パラメータとして必要で、 を使用して呼び出していますMyClassDer。したがって、コンパイル時に、他のオーバーロードされた関数を解決し、それとリンクします。他の関数はテンプレート化されているため、コンパイラがそれとリンクしても問題はありません。

したがって、オブジェクトを渡したい場合MyClassDerでも、ポリモーフィズムを使用してそれを行うことができます。

MyClass *myClassDer = new MyClassDer;
func(*myClassDer);
于 2013-09-24T13:07:00.533 に答える