web-dev-qa-db-ja.com

Javaパラメータ/戻り値の型に<T extends Class>を使用するのと同等のC ++

Javaでは、パラメーターと同じ型のオブジェクトを返し、特定のクラスを拡張する関数を作成するには、次のように入力します。

public <T extends MyClass> T foo(T bar) {...}

これに相当するC++はありますか?

言い換えれば、特定のクラスを拡張するクラスを受け取り、その同じ型を返す関数を作成するにはどうすればよいですか? (これは抽象/純粋仮想クラスの目的のためです)。

21
ricky3350

enable_if C++ 11以降を使用できる場合はここに

template<typename T, typename std::enable_if<std::is_base_of<MyClass, T>::value>::type* = nullptr>
T Foo(T bar)
{
    return T();
}

例えば:

class MyClass
{
public:
    int a = 1;
};

class Derived : public MyClass
{
public:
    int b = 2;
};

class NotDerived
{
public:
    int b = 3;
};

template<typename T, typename std::enable_if<std::is_base_of<MyClass, T>::value>::type* = nullptr>
T Foo(T bar)
{
    return T();
}

int main()
{
    Derived d;
    NotDerived nd;
    std::cout << Foo(d).b << std::endl;; // works
    //std::cout << (Foo(nd)).b << std::endl;; //compiler error

    return 0;
}

ライブデモ

13
AndyG

技術的には、他の回答が示すように、コンパイル時に特定のタイプのサブタイプに制限する方法があります。ただし、ほとんどの場合、

template <typename T> T foo(T bar) {...}

境界を指定する必要はありません。

Javaでは、ジェネリッククラスまたはメソッドはその使用とは別にコンパイルされるため、ジェネリックには境界が必要です。ジェネリッククラスまたはジェネリックメソッドは、一度コンパイルされて、バイトコードの単一バージョンになり、単一バージョンは、呼び出し元がスローする、宣言の境界を満たす任意の引数を処理できます。

コンパイラーは、Tが何であるかを知らずに、メソッド呼び出し、フィールドアクセスなど、メソッドの本体でTの型の使用を型チェックする必要があるため、バインドを提供する必要があります。そのため、コンパイラーは、たとえばメソッド呼び出しが有効であることを満足させることができます。これは、その範囲を満たすすべての型で定義されているためです。たとえば、メソッドの本体にbar.baz()という式がある場合、コンパイラーは、タイプMyClass(したがって、そのすべてのサブタイプ)がメソッド.baz();境界を指定しなかった場合、コンパイラはObject(暗黙の上限)にメソッド.baz()がないことを報告します。

C++テンプレートは異なります。テンプレート化されたクラスまたは関数は、それが使用されるすべての異なる型引数に対して「インスタンス化」(再コンパイル)されます。したがって、特定のTの関数の本体をコンパイルするときに、コンパイラはTが何であるかを認識しており、その型の使用を直接型チェックすることができます。

したがって、関数の本体にbar.baz()という式がある場合は、それで問題ありません。 TMyClassを拡張する型としてこの関数を使用した場合、そのような型には.baz()があるため、正常にコンパイルされます。 .baz()を持たない型でこの関数を使用すると、その使用法でコンパイルに失敗します。 MyClassを拡張しない型で関数を誤って使用したが、.baz()があり、パラメーターの型と戻り値の型が使用方法と一致している場合も、コンパイルされます。しかし、それは必ずしも悪いことではありません。 C++テンプレートは通常、型階層では使用されませんが、型が提供する必要があるものに対する要件とともに使用されます。したがって、たとえば、並べ替えアルゴリズムでは、コンテナや要素のタイプが特定のタイプを拡張する必要はなく、コンテナが特定の機能(ランダムアクセス添字演算子など)を提供し、要素タイプが特定の機能(例:小なり演算子)。

15
newacct

承認された回答についてコメントすることはできないので、それに基づいて新しい回答を提供します。

テンプレートパラメータは、enable_if条件はnullptrではなく デフォルトのタイプテンプレートパラメーター になります。

template<typename T, typename = std::enable_if<std::is_base_of<MyClass, T>::value>>
6
Matthew Borger