web-dev-qa-db-ja.com

戻り値に従ってC ++関数をオーバーロードします

パラメータに従って関数をオーバーロードできることは誰もが知っています。

int mul(int i, int j) { return i*j; }
std::string mul(char c, int n) { return std::string(n, c); } 

戻り値に従って関数をオーバーロードできますか?戻り値の使用方法に応じて異なるものを返す関数を定義します。

int n = mul(6, 3); // n = 18
std::string s = mul(6, 3); // s = "666"
// Note that both invocations take the exact same parameters (same types)

最初のパラメーターは0〜9であり、入力を確認したり、エラー処理を行ったりする必要はないと想定できます。

35
Motti
class mul
{
public:
    mul(int p1, int p2)
    {
        param1 = p1;
        param2 = p2;
    }
    operator int ()
    {
        return param1 * param2;
    }

    operator std::string ()
    {
        return std::string(param2, param1 + '0');
    }

private:
    int param1;
    int param2;
};

それを使うわけではありません。

41
Coincoin

使用するバージョンをコンパイラーに指示する必要があります。 C++では、3つの方法で実行できます。

次のように入力して、通話を明示的に区別します

文字を待機している関数に整数を送信し、「6」の文字値が6ではなく54(ASCII)の場合に誤って数値6を送信したため、多少不正行為をしました。

std::string mul(char c, int n) { return std::string(n, c); }

std::string s = mul(6, 3); // s = "666"

もちろん、正しい解決策は

std::string s = mul(static_cast<char>(54), 3); // s = "666"

あなたが解決策を望まなかったとしても、これは言及する価値があったと思います。

ダミーポインタで呼び出しを明示的に区別する

各関数にダミーパラメータを追加して、コンパイラに適切な関数を選択させることができます。最も簡単な方法は、戻りに必要なタイプのNULLダミーポインタを送信することです。

int mul(int *, int i, int j) { return i*j; }
std::string mul(std::string *, char c, int n) { return std::string(n, c); }

コードで使用できるもの:

int n = mul((int *) NULL, 6, 3); // n = 18
std::string s = mul((std::string *) NULL, 54, 3); // s = "666"

戻り値をテンプレート化することにより、呼び出しを明示的に区別します

このソリューションでは、インスタンス化された場合にコンパイルされないコードを使用して「ダミー」関数を作成します。

template<typename T>
T mul(int i, int j)
{
   // If you get a compile error, it's because you did not use
   // one of the authorized template specializations
   const int k = 25 ; k = 36 ;
}

この関数はコンパイルされないことに注意してください。これは、テンプレートの特殊化を通じて一部の限定された関数のみを使用するため、良いことです。

template<>
int mul<int>(int i, int j)
{
   return i * j ;
}

template<>
std::string mul<std::string>(int i, int j)
{
   return std::string(j, static_cast<char>(i)) ;
}

したがって、次のコードがコンパイルされます。

int n = mul<int>(6, 3); // n = 18
std::string s = mul<std::string>(54, 3); // s = "666"

しかし、これはしません:

short n2 = mul<short>(6, 3); // error: assignment of read-only variable ‘k’

戻り値2をテンプレート化することにより、呼び出しを明示的に区別します。

ねえ、あなたもだましました!

そうです、2つの「オーバーロードされた」関数に同じパラメーターを使用しました。しかし、あなたは不正行為を始めました(上記を参照)...

^ _ ^

さらに深刻なことに、異なるパラメーターが必要な場合は、より多くのコードを記述し、あいまいさを避けるために関数を呼び出すときに適切な型を明示的に使用する必要があります。

// For "int, int" calls
template<typename T>
T mul(int i, int j)
{
   // If you get a compile error, it's because you did not use
   // one of the authorized template specializations
   const int k = 25 ; k = 36 ;
}

template<>
int mul<int>(int i, int j)
{
   return i * j ;
}

// For "char, int" calls
template<typename T>
T mul(char i, int j)
{
   // If you get a compile error, it's because you did not use
   // one of the authorized template specializations
   const int k = 25 ; k = 36 ;
}

template<>
std::string mul<std::string>(char i, int j)
{
   return std::string(j, (char) i) ;
}

そして、このコードは次のように使用されます。

int n = mul<int>(6, 3); // n = 18
std::string s = mul<std::string>('6', 3); // s = "666"

そして次の行:

short n2 = mul<short>(6, 3); // n = 18

それでもコンパイルされません。

結論

私はC++が大好きです...

:-p

50
paercebal

mulをクラスではなく実関数にしたい場合は、中間クラスを使用できます。

class StringOrInt
{
public:
    StringOrInt(int p1, int p2)
    {
        param1 = p1;
        param2 = p2;
    }
    operator int ()
    {
        return param1 * param2;
    }

    operator std::string ()
    {
        return std::string(param2, param1 + '0');
    }

private:
    int param1;
    int param2;
};

StringOrInt mul(int p1, int p2)
{
    return StringOrInt(p1, p2);
}

これにより、mulを関数としてstdアルゴリズムに渡すなどのことができます。

int main(int argc, char* argv[])
{
    vector<int> x;
    x.Push_back(3);
    x.Push_back(4);
    x.Push_back(5);
    x.Push_back(6);

    vector<int> intDest(x.size());
    transform(x.begin(), x.end(), intDest.begin(), bind1st(ptr_fun(&mul), 5));
    // print 15 20 25 30
    for (vector<int>::const_iterator i = intDest.begin(); i != intDest.end(); ++i)
        cout << *i << " ";
    cout << endl;

    vector<string> stringDest(x.size());
    transform(x.begin(), x.end(), stringDest.begin(), bind1st(ptr_fun(&mul), 5));
    // print 555 5555 55555 555555
    for (vector<string>::const_iterator i = stringDest.begin(); i != stringDest.end(); ++i)
        cout << *i << " ";
    cout << endl;

    return 0;
}
23
Eclipse

番号。

呼び出し元は戻り値を使用して何でも(または何も)できないため、戻り値でオーバーロードすることはできません。考えてみましょう:

mul(1, 2);

戻り値は破棄されるだけなので、戻り値だけに基づいてオーバーロードを選択する方法はありません。

21
MrZebra

クラス間の暗黙的な変換を使用します。

class BadIdea
{
  public:
    operator string() { return "silly"; }
    operator int() { return 15; }
};

BadIdea mul(int, int)

しかし、あなたはその考え、ひどい考えを得る。

8
Pieter

Mulをクラス、mul(x、y)をそのコンストラクターとし、いくつかのキャスト演算子をオーバーロードします。

4

戻り値のみに基づいて関数をオーバーロードすることはできません。

ただし、厳密に言えば、これはオーバーロードされた関数ではありませんが、変換演算子をオーバーロードするクラスのインスタンスの結果として、関数から戻る可能性があります。

3
Franci Penov

パラメータをキャプチャするだけの奇妙なタイプのFooを返すことができ、Fooには暗黙の演算子intと演算子文字列があり、実際にはオーバーロードではなく、暗黙の変換トリックではありますが、「機能」すると思います。

2
Brian

短くて単純な答えはNOです。 C++では、要件は次のとおりです。

1:関数の名前は同じでなければなりません
2:引数のセットは異なっていなければなりません
*戻り値の型は同じでも異なっていてもかまいません

//This is not valid
    int foo();
    float foo();

    typedef int Int;

    int foo(int j);
    int foo(Int j);

//Valid:
   int foo(int j);
   char* foo(char * s);
   int foo(int j, int k);
   float foo(int j, float k);
   float foo(float j, float k);
1

うーん、次の コードプロジェクトの記事 はあなたが求めていることをしているようです。魔法でなければなりません;)

1
SmacL

上記のファンクターソリューションを使用できます。 C++は、const以外の関数ではこれをサポートしていません。 constに基づいてオーバーロードできます。

0
mempko

私の知る限り、あなたはできません(しかし、大きな残念です...)。回避策として、代わりに「out」パラメーターを定義し、そのパラメーターをオーバーロードすることができます。

0
xtofl

C++ではありません。上記の例で得られるのは、stringが理解できるもの、おそらくcharにキャストされたintである戻り値です。 ASCII 18または "デバイス制御2"になります。

0
warren