3

私はそのような関数定義を持っています

template <typename T>
auto print(T t) -> decltype(t.print()) {
    return t.print();
}

アイデアは、引数が型Tでなければならず、print関数を持たなければならないということです。このprint関数は何でも返すことができ、 の必要性を説明していますdecltype。たとえば、次のことができます。

struct Foo
{
    int print()
    {
        return 42;
    }
};

struct Bar
{
    std::string print()
    {
        return "The answer...";
    }
};

...

std::cout << print(Foo()) << std::endl;    
std::cout << print(Bar()) << std::endl;
/* outputs: 
42
The answer...
*/

テンプレートはランタイムのインスタンス化を行うことができず、クラスを基本クラスから派生させ、その型を決定して使用するテンプレート引数を確認できることを読みました。ただし、non-classタイプに対してこれを行うにはどうすればよいですか? アイデアは、次のものを持つことができることです:

template <typename T>
T print(T t) {
    return t;
}

同様に、しかしこれは私にあいまいな過負荷エラーを与えます。予選は機能しませんprint<Foo>。もう 1 つの問題は、次のようなファンクタがある場合はどうなるかということです。

struct Foo
{
  virtual int print();
  operator int() const
  {
    return 42;
  }
};

それは今どのように決定されますか?

私の質問は、これらすべてのあいまいさをテンプレートで解決することは可能ですか、それとも冗長なコードをたくさん書く必要があるのでしょうか?

テスト

テストを段階的に追加し、編集した各ソリューションを下からコピー/貼り付けました。結果は次のとおりです。

次のクラスを使用します。

struct Foo
{
    int print()
    {
        return 42;
    }

    operator int() const
    {
        return 32;
    }
};

struct Bar
{
    std::string print()
    {
        return "The answer...";
    }

    operator int() const
    {
        return (int)Foo();
    }
};

struct Baz
{
    operator std::string() const
    {
        return std::string("The answer...");
    }
};

そして、次のテスト出力:

std::cout << print(Foo()) << std::endl;    
std::cout << print(Bar()) << std::endl;
std::cout << print(42) << std::endl;
std::cout << print((int)Foo()) << std::endl;
std::cout << print("The answer...") << std::endl;
std::cout << print(std::string("The answer...")) << std::endl;
std::cout << print((int)Bar()) << std::endl;
std::cout << print((std::string)Baz()) << std::endl;

どちらも正しく出力されます:

42
The answer...
42
32
The answer...
The answer...
32
The answer...
4

2 に答える 2

5

print()このようなメンバー関数が存在する場合は、入力に対してメンバー関数を呼び出す次のアプローチを採用できます。それ以外の場合は、入力自体を返します。

namespace detail
{
    template<typename T, typename = void>
    struct print_helper
    {
        static T print(T t) {
            return t;
        }
    };

    template<typename T>
    struct print_helper<T, decltype(std::declval<T>().print(), (void)0)>
    {
        static auto print(T t) -> decltype(t.print()) {
            return t.print();
        }
    };
}

template<typename T>
auto print(T t) -> decltype(detail::print_helper<T>::print(t))
{
    return detail::print_helper<T>::print(t);
}

これが実際のです。

于 2013-05-12T08:53:01.827 に答える
2

直接印刷したいタイプごとに手動オーバーロードを使用した簡単なソリューション:

を呼び出す最初の実装を定義しますT::print()。オーバーロードを使用して、この関数を持たないすべての型の代替実装を指定します。この解決策はお勧めしませんが、理解するのは非常に簡単です。

template<typename T>
auto print(T t) -> decltype(t.print()) {
    return t.print();
}

int print(int t) {
    return t;
}
std::string print(std::string t) {
    return t;
}
// ... and so on, for each type you want to support ...

そこにある場合にのみ自動的に使用するSFINAEを使用したより高度なソリューション:T::print()

まず、型に function があるかどうかを決定できる trait を定義しますprint()。基本的に、この特性は、ヘルパー クラス ( ) で行われる決定に応じて、std::true_typeまたはから継承されます。次に、2 つのケースのうちの1 つだけを定義し、もう 1 つのケースを非表示にするコンパイル時の決定で、この型特性を使用します(したがって、これはオーバーロードではありません)。std::false_type_test_printenable_if

// Type trait "has_print" which checks if T::print() is available:
struct _test_print {
    template<class T> static auto test(T* p) -> decltype(p->print(), std::true_type());
    template<class>   static auto test(...)  -> std::false_type;
};
template<class T> struct has_print : public decltype(_test_print::test<T>(0)) {};


// Definition of print(T) if T has T::print():
template<typename T>
auto print(T t) -> typename std::enable_if<has_print<T>::value, decltype(t.print())>::type {
    return t.print();
}

// Definition of print(T) if T doesn't have T::print():
template<typename T>
auto print(T t) -> typename std::enable_if<!has_print<T>::value, T>::type {
    return t;
}

ライブデモをご覧ください。

于 2013-05-12T08:57:02.250 に答える