関数が定義されているタイプに対してのみ、関数テンプレート内で関数を実行します


13

入力としてさまざまなタイプを受け取る関数テンプレートがあります。それらのタイプのうち、そのうちの1つだけがgetInt()機能を持っています。したがって、コードでそのタイプに対してのみ関数を実行する必要があります。解決策を提案してください。ありがとう

#include <type_traits>
#include <typeinfo>

class X {
    public:
    int getInt(){
        return 9;
    }
};

class Y{

};

template<typename T>
void f(T& v){
    // error: 'class Y' has no member named 'getInt'
    // also tried std::is_same<T, X>::value 
    if(typeid(T).name() == typeid(X).name()){
        int i = v.getInt();// I want this to be called for X only
    }
}

int main(){
    Y y;
    f(y);
}

あなたの問題とは無関係ですが、type_info構造には等価比較演算子があるためtypeid(T) == typeid(X)、同様に機能するはずです。
一部のプログラマー

5
使用:if constexpr条件付きis_same_v<T,X>
rafix07

これに対する解決策は、Conceptsを使用して、今年後半に正式にさらに洗練される予定です。現時点ではあまり役に立ちませんが、私は知っています。
スウィーニッシュ

問題を解決する方法はたくさんあります。上記のカップル。さまざまなバリアントの特性を使用して、型に呼び出し可能getIntメンバーがあるかどうかを確認することもできます。少し検索するだけで、構造体またはクラスに特定のメンバー関数があるかどうかを確認する方法について、stackoverflow.comだけでかなりの数の質問があるはずです。
プログラマー

回答:


10

だけでなく、ffunctionメンバーを持つすべての型の関数を呼び出すことができるようにしたい場合は、function に2つのオーバーロードを宣言できます。getIntXf

  1. getIntクラスを含む、メンバー関数を持つ型の場合X

  2. classを含む他のすべてのタイプの場合Y

C ++ 11 / C ++ 17ソリューション

それを念頭に置いて、次のようなことができます。

#include <iostream>
#include <type_traits>

template <typename, typename = void>
struct has_getInt : std::false_type {};

template <typename T>
struct has_getInt<T, std::void_t<decltype(((T*)nullptr)->getInt())>> : std::is_convertible<decltype(((T*)nullptr)->getInt()), int>
{};

class X {
public:
    int getInt(){
        return 9;
    }
};

class Y {};

template <typename T,
          typename std::enable_if<!has_getInt<T>::value, T>::type* = nullptr>
void f(T& v) {
    // only for Y
    std::cout << "Y" << std::endl;
}

template <typename T,
          typename std::enable_if<has_getInt<T>::value, T>::type* = nullptr>
void f(T& v){
    // only for X
    int i = v.getInt();
    std::cout << "X" << std::endl;
}

int main() {
    X x;
    f(x);

    Y y;
    f(y);
}

ライブでご覧ください。

これstd::void_tはC ++ 17で導入されていますが、C ++ 11に限定されている場合はvoid_t、自分で実装するのが本当に簡単です。

template <typename...>
using void_t = void;

そしてここにC ++ 11バージョンのライブがあります。

C ++ 20には何がありますか?

C ++ 20には多くの優れた機能があり、そのうちの1つは概念です。上記のC ++ 11 / C ++ 14 / C ++ 17に有効なものは、C ++ 20で大幅に削減できます。

#include <iostream>
#include <concepts>

template<typename T>
concept HasGetInt = requires (T& v) { { v.getInt() } -> std::convertible_to<int>; };

class X {
public:
    int getInt(){
        return 9;
    }
};

class Y {};

template <typename T>
void f(T& v) {
    // only for Y
    std::cout << "Y" << std::endl;
}

template <HasGetInt T>
void f(T& v){
    // only for X
    int i = v.getInt();
    std::cout << "X" << std::endl;
}

int main() {
    X x;
    f(x);

    Y y;
    f(y);
}

ライブでご覧ください。


C ++ 17より前のバージョンでは、この実装はvoid_t一部の古いコンパイラに問題を引き起こします(リンクで示されています)。
Jarod42

厳密に2つのオーバーロードを記述する必要はありません( "need"を "can"で置き換える方がはるかに優れています)
idclev 463035818

@ idclev463035818が更新されました。ありがとう
NutCracker

1
@SSAnne更新
NutCracker

1
コンセプトの定義は正確ではありません。結果をintに割り当てるので、コンセプトはtemplate<typename T> concept HasGetInt = requires (T& v) { {v.getInt()} -> std::convertible_to<int>; };
Hui

8

あなたはif constexprC ++ 17から使うかもしれません:

template<typename T>
void f(T& v){
    if constexpr(std::is_same_v<T, X>) { // Or better create trait has_getInt
        int i = v.getInt();// I want this to be called for X only
    }
    // ...
}

以前は、オーバーロードとSFINAEまたはタグディスパッチを使用する必要があります。


if constexprC ++ 17の機能です。
Andrey Semashev

ただし、これはクラスでのみ機能しますX
NutCracker

質問はC ++ 11 / C ++ 14のみに更新されました
NutCracker

@NutCracker:タグ/質問を更新して既存の回答を無効にするのは良くありません...(それについての警告が問題なくても)。
Jarod42

タグを更新しました...質問のタイトルはOPによって更新されました
NutCracker

7

シンプルにして過負荷にします。少なくともC ++ 98以降で動作しています...

template<typename T>
void f(T& v)
{
    // do whatever
}

void f(X& v)
{
    int result = v.getInt();
}

getInt機能を持つタイプが1つしかない場合は、これで十分です。それ以上あると、それはもはやそれほど単純ではありません。これを行うにはいくつかの方法があります。1つは次のとおりです。

struct PriorityA { };
struct PriorityB : PriorityA { };

template<typename T>
void f_impl(T& t, PriorityA)
{
    // generic version
}

// use expression SFINAE (-> decltype part)
// to enable/disable this overload
template<typename T>
auto f_impl(T& t, PriorityB) -> decltype(t.getInt(), void())
{
    t.getInt();
}

template<typename T>
void f(T& t)
{
    f_impl(t, PriorityB{ } ); // this will select PriorityB overload if it exists in overload set
                              // otherwise PriorityB gets sliced to PriorityA and calls generic version
}

診断出力付きのライブ例。


1
この場合、オーバーロードが1つしかないため(これはX)機能しますが、getInt将来的にメンバーを持つ同様のタイプがさらに存在する場合、これはあまり良い方法ではありません。あなたはおそらくそれに注意したいでしょう
NutCracker

@NutCrackerそうでしたか?
jrok
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.