5

次の関数テンプレートがあるとします。

#include <vector>
#include <utility>

struct Base { };
struct Derived : Base { };

// #1
template <typename T1, typename T2>
void f(const T1& a, const T2& b)
{
};

// #2
template <typename T1, typename T2>
void f(const std::vector<std::pair<T1, T2> >& v, Base* p)
{
};

次のコードが常にオーバーロード #2 ではなくオーバーロード #1 を呼び出すのはなぜですか?

int main()
{
    std::vector<std::pair<int, int> > v;
    Derived derived;

    f(100, 200);  // clearly calls overload #1
    f(v, &derived);         // always calls overload #1

    return 0;
}

fの 2 番目のパラメーターが の派生型であることを考えると、Baseオーバーロード #1 のジェネリック型よりも一致するオーバーロード #2 をコンパイラが選択することを期待していました。

これらの関数を書き直して、ユーザーがmain関数に表示されるようにコードを記述できるようにするために使用できる手法はありますか (つまり、引数の型のコンパイラ推定を利用します)。

4

3 に答える 3

12

これを行うことができます:

f(v, static_cast<Base*>(&derived));

または、SFINAE を使用して、最初の関数を選択候補から削除します。

// Install boost library and add these headers:
#include <boost/utility/enable_if.hpp>
#include <boost/type_traits.hpp>

// #1 - change it to look like this (note the keyword void changed positions)
template <typename T1, typename T2>
typename boost::disable_if<
   typename boost::is_convertible<T2, Base*>, void>::type
f(const T1& a, const T2& b)
{
};

// #2 - this one can stay the same
template <typename T1, typename T2>
void f(const std::vector<std::pair<T1, T2> >& v, Base* p)
{
};
于 2009-10-14T18:11:21.370 に答える
8

f の 2 番目のパラメーターが Base の派生型である場合

そのようなものに変換できますが、Derived* です。最初のテンプレート関数は変換を必要とせず、2 番目のテンプレート関数は変換を必要とするため、最初のテンプレート関数が選択されます。

これは2番目を選択します:

f(v, static_cast<Base*>(&derived));

ちなみに、 をmain返しますint

于 2009-10-14T17:53:57.083 に答える
1

コンパイラによって多かれ少なかれ適切に実装されているKoenig Lookupに関する明白なトピック(特に古いものはかなり問題があります) に加えて、テンプレートの特殊化に関するいくつかの落とし穴があります。

特殊化では、型が正確に一致する必要があります (std がこれをどのように定義するかはわかりませんが、私の経験 [gcc、msvc] から派生クラスは一致しません)。Base* に醜いキャストを追加すると、意図したとおりに動作し、オプションで Derived に別の特殊化を追加できます...

于 2009-10-14T18:09:12.747 に答える