戻り値の型によるオーバーロード


81

私はこのトピックについてSOでいくつかの質問を読みましたが、それでも私には混乱しているようです。私はC ++を学び始めたばかりで、テンプレートや演算子のオーバーロードなどについてはまだ勉強していません。

今、過負荷にする簡単な方法があります

class My {
public:
    int get(int);
    char get(int);
}

テンプレートや奇妙な動作なし?または私はただ

class My {
public:
    int get_int(int);
    char get_char(int);
}



1
@AdamV、私はあなたのコメントが本当に好きです。短いですが完全にしっかりしています。
Pouya 2014

@Adam V実際には、オーバーロードされた関数のアドレスを取得することには、すでにそのようなあいまいさがあります。そのような場合、式には何らかのタイプの期待があるはずです。プログラムが1つもない場合は、プログラムの形式が正しくありません。そして、これはすでに実装されています。戻り値の型による関数のオーバーロードを実装するために同じルールを使用することはそれほど難しいことではないと思います。したがって、具体的な例では、返されるタイプのキャストによってあいまいさが取り除かれます。int戻り値を使用したインスタンス化は(int)get(9)、次のcharようになります(char)get(9)
anArrayOfFunctions 2015

ここにたどり着いたら、Luchianが提案したような2つの異なる関数名を考えるのが最善の選択だと思います。
Kemin Zhou

回答:


101

いいえ、ありません。戻り値の型に基づいてメソッドをオーバーロードすることはできません。

過負荷の解決では、関数のシグネチャが考慮されます。関数シグネチャは次のもので構成されます。

  • 関数名
  • cv-qualifiers
  • パラメータタイプ

そしてここに引用があります:

1.3.11署名

過負荷解決に関与する関数に関する情報(13.3):そのパラメータータイプリスト(8.3.5)、および関数がクラスメンバーの場合は、関数自体とクラスのcv修飾子(存在する場合)メンバー関数が宣言されている場所。[...]

オプション:

1)メソッド名を変更します。

class My {
public:
    int getInt(int);
    char getChar(int);
};

2)出力パラメータ:

class My {
public:
    void get(int, int&);
    void get(int, char&);
}

3)テンプレート...この場合はやり過ぎです。


17
戻り値の型で通常の関数をオーバーロードすることはできませんが、コンパイラーは結果の型に基づいて変換演算子から選択します。これを利用して、戻り値の型でオーバーロードしたかのように効果的に機能するプロキシを作成できます。
James Kanze 2012年

2
@JeffPigarelliテンプレートソリューションとは、メンバーテンプレートを意味しますMy::get<T>(int)。これは有効な代替手段です_if1)すべて同じ基本コードで多くの異なるタイプを処理する必要があります(たとえばboost::lexical_cast<T>( someStringValue )、または他のテンプレートからこれらの関数を呼び出すことができる必要があります(myMy.get<T>( i )Tはこの他のテンプレートの引数です) 。Luchianが言うようにそうでなければ、彼らはやり過ぎだ。
ジェームズ・観世

39
注意の理由あなたが過負荷戻り値の型に基づいてすることはできませんが、C ++を使用して、関数呼び出しの値を破棄できることです。したがって、単にmy.get(0);コンパイラを呼び出した場合、実行するコードを決定する方法はありません。
ベンザド2012年

9
その場合、@ benzadoは、そのような場合にのみコンパイラエラーをスローする必要があります。そうでない場合は、他の多くのシナリオですでに行われいるように、型を推測する必要があります。
rr- 2015

2
同じ理由で@benzadoはvoid foo(int x = 0) {} void foo(double x = 0) {}許可されるべきではありません。しかし、そうではありません。コンパイラが実際に(foo())を区別できない場合にのみ、エラーが発生します
maximum_prime_is_463035818 2018

82

可能ですが、初心者におすすめのテクニックかどうかはわかりません。他の場合と同様に、関数の選択を戻り値の使用方法に依存させたい場合は、プロキシを使用します。最初にgetCharとのような関数を定義しgetInt、次に次のget()ようなプロキシを返すジェネリックを定義します。

class Proxy
{
    My const* myOwner;
public:
    Proxy( My const* owner ) : myOwner( owner ) {}
    operator int() const
    {
        return myOwner->getInt();
    }
    operator char() const
    {
        return myOwner->getChar();
    }
};

必要な数のタイプに拡張します。


10
+1、ただしコーナーケースですが、変換演算子は実際には戻り値の型でオーバーロードされており、あらゆる場所でこの機能を利用できます。
Matthieu M.

3
@MatthieuM。ほぼすべての場所ですが、暗黙の変換に関する通常の警告があります。他の方法では存在しない曖昧さを導入するリスクがあります。ただし、プロキシの場合、リスクは小さいと思います---暗黙的な変換が必要な場合を除いて、プロキシタイプのインスタンスはありません。プロキシでの変換は、1つのユーザー定義の変換としてカウントされることにも注意してください。が必要std::stringで、プロキシが提供するだけの場合operator char const*()、それは機能しません。
James Kanze 2012年

なぜここでプロキシを使用するのか、プロキシが必須であるケースは考えられません。提供してもらえますか?ありがとう!
陳力2017年

8

いいえ、戻り値の型でオーバーロードすることはできません。パラメータタイプ、およびconst / volatile修飾子によってのみ。

1つの代替方法は、参照引数を使用して「返す」ことです。

void get(int, int&);
void get(int, char&);

おそらくテンプレートを使用するか、2番目の例のように別の名前の関数を使用します。


戻り値の型がintエラーコードであるEFIAPI。
コールジョンソン

1
char型とint型は暗黙的に変換される可能性があることに注意してください。
ケミン周

5

あなたはこのように考えることができます:

あなたが持っている:

  int get(int);
  char get(int);

また、呼び出し中に関数の戻り値を収集することは必須ではありません。

今、あなたは呼び出す

  get(10);  -> there is an ambiguity here which function to invoke. 

したがって、戻り値の型に基づいてオーバーロードが許可されている場合は意味がありません。


4

古いスレッドを復活させましたが、ref-qualifiersによるオーバーロードについて誰も言及していなかったことがわかります。Ref-qualifiersはC ++ 11で追加された言語機能であり、私は最近それを見つけました-それは例えばcv-qualifiersほど普及していません。主なアイデアは、メンバー関数が右辺値オブジェクトで呼び出される場合と、左辺値オブジェクトで呼び出される場合の2つのケースを区別することです。基本的に次のように書くことができます(私はOPのコードを少し変更しています):

#include <stdio.h>

class My {
public:
    int get(int) & { // notice &
        printf("returning int..\n");
        return 42;
    }
    char get(int) && { // notice &&
        printf("returning char..\n");
        return 'x';
    };
};

int main() {
    My oh_my;
    oh_my.get(13); // 'oh_my' is an lvalue
    My().get(13); // 'My()' is a temporary, i.e. an rvalue
}

このコードは、次の出力を生成します。

returning int..
returning char..

もちろん、cv-qualifiersの場合と同様に、両方の関数が同じ型を返す可能性があり、オーバーロードは引き続き成功します。


4

前に述べたように、この場合、テンプレートはやり過ぎですが、それでも言及する価値のあるオプションです。

class My {
public:
    template<typename T> T get(int);
};

template<> int My::get<int>(int);
template<> char My::get<char>(int);

3

この問題の他のコメントのほとんどは技術的に正しいですが、あなたができる効果的に、戻り値をオーバーロードする場合は、あなたが入力パラメータをオーバーロードするとそれを組み合わせます。例えば:

class My {
public:
    int  get(int);
    char get(unsigned int);
};

デモ:

#include <stdio.h>

class My {
public:
    int  get(         int x) { return 'I';  };
    char get(unsinged int x) { return 'C';  };
};

int main() {

    int i;
    My test;

    printf( "%c\n", test.get(               i) );
    printf( "%c\n", test.get((unsigned int) i) );
}

これの結果は次のとおりです。

I 
C

6
これは関数のシグネチャを完全に変更するので、戻り値の型によってオーバーロードするのではなく、単にオーバーロードするだけです
Dado

このメソッドを正常に使用して、本番環境を実行しているC ++ JSONAPIのさまざまな値を返しました。うまくいきました!技術的には戻り値の型によるオーバーロードはありませんが、同じ関数名で異なる戻り値の型の意図を実現し、有効で明確なC ++であり、オーバーヘッドがほとんどありません(関数呼び出しでの単一変数のインスタンス化)。
guidotex 2018

これは戻り値の型によるオーバーロードではありませんが、機能します。私に「卑劣な」と言わせます
マーカス

2

C ++では戻り値の型でオーバーロードする方法はありません。テンプレートを使用せずに、使用するget_intと、get_charあなたができる最善となります。


念のために言っておきますが、何template <class T> T get(int)かうまくいくでしょうか?
Niklas B.

4
はい、@ Niklasですが、get<int>またはと呼ぶ必要があります。これは、他のテンプレート機能も使用していない場合は、get<char>あまり効果がget_intありget_charません。
ロブ・ケネディ

@Rob:ええと、コンパイラはTあなたがのようなものを持っているかどうかを判断できますT get(T)。を呼び出すget('a')と、コンパイラはそれTがaであるcharと推測し、明示的にを呼び出す必要はありませんget<char>('a')。標準かどうかはわかりませんが、標準かどうかはわかりません。参考までに、GCCとClangの両方がこれをサポートしています。
netcoder 2012年

1
これは完全に標準の@Netcoderですが、コンパイラが戻り値の型だけを推測するわけではありません。あなたの例では、コンパイラは引数の型を推測し、それを知ると、T戻り値の型を含む他のすべての場所の値を入力します。TNiklasの最初のコメントで、関数を推測するコンパイラの例を示すことを期待していました。
ロブ・ケネディ

2

戻り値の型に基づいてメソッドをオーバーロードすることはできません。最善の策は、2番目のコードスニペットのように、構文がわずかに異なる2つの関数を作成することです。


0

関数の戻り値の型に基づいて関数をオーバーロードすることはできません。この関数が取る引数のタイプと数に基づいて、overleadすることができます。


0

プロキシを使用してJamesKanzeの回答を使用しました。

https://stackoverflow.com/a/9569120/262458

void *で多くの醜いstatic_castを使用することを避けたかったので、これを行いました。

#include <SDL_joystick.h>
#include <SDL_gamecontroller.h>

struct JoyDev {
    private:
        union {
            SDL_GameController* dev_gc = nullptr;
            SDL_Joystick*       dev_js;
        };
    public:
        operator SDL_GameController*&() { return dev_gc; }
        operator SDL_Joystick*&()       { return dev_js; }

        SDL_GameController*& operator=(SDL_GameController* p) { dev_gc = p; return dev_gc; }
        SDL_Joystick*&       operator=(SDL_Joystick* p)       { dev_js = p; return dev_js; }
};

struct JoyState {
    public:
        JoyDev dev;
};

int main(int argc, char** argv)
{
    JoyState js;

    js.dev = SDL_JoystickOpen(0);

    js.dev = SDL_GameControllerOpen(0);

    SDL_GameControllerRumble(js.dev, 0xFFFF, 0xFFFF, 300);

    return 0;
}

完璧に動作します!

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