40

我们都知道可以根据参数重载一个函数:

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 之间,无需验证输入或进行任何错误处理。

4

16 回答 16

55

你必须告诉编译器使用哪个版本。在 C++ 中,您可以通过三种方式进行操作。

通过键入明确区分呼叫

您有点作弊,因为您向等待字符的函数发送了一个整数,并且当 '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

嘿,你也作弊了!

对,我确实为两个“重载”函数使用了相同的参数。但你确实开始作弊(见上文)......

^_^

更严重的是,如果您需要不同的参数,那么您将编写更多代码,然后在调用函数时必须显式使用正确的类型以避免歧义:

// 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

于 2008-10-23T08:17:38.520 回答
46
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;
};

不是说我会用那个。

于 2008-10-22T15:18:16.893 回答
23

如果你想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;
}
于 2009-01-09T21:02:03.143 回答
21

不。

您不能通过返回值重载,因为调用者可以用它做任何事情(或什么都不做)。考虑:

mul(1, 2);

返回值只是被丢弃了,因此它无法仅根据返回值选择重载。

于 2008-10-22T15:05:46.543 回答
8

在中间类中使用隐式转换。

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

BadIdea mul(int, int)

你明白了,但可怕的想法。

于 2008-10-22T15:13:31.253 回答
4

让 mul 是一个类, mul(x, y) 是它的构造函数,并重载一些强制转换运算符。

于 2008-10-22T15:19:32.270 回答
3

您不能仅根据返回值重载函数。

但是,虽然严格来说这不是一个重载函数,但您可以从函数中返回一个重载转换运算符的类的实例。

于 2008-10-22T15:06:17.947 回答
2

我想你可以让它返回一些奇怪的类型 Foo ,它只捕获参数,然后 Foo 有一个隐式运算符 int 和运算符 string,它会“工作”,尽管它不会真正重载,而是一个隐式转换技巧。

于 2008-10-22T15:15:11.563 回答
2

简而言之,答案是否定的。在 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);
于 2011-07-19T22:06:34.370 回答
0

据我所知,你不能(可惜……)。作为一种解决方法,您可以改为定义一个“out”参数,然后重载该参数。

于 2008-10-22T15:04:32.397 回答
0

不在 C++ 中。你在上面的例子中得到的是返回值,它是一个 int 转换成string可以理解的东西,很可能是一个char. 这将是 ASCII 18 或“设备控制 2”。

于 2008-10-22T15:08:33.010 回答
0

您可以使用上面的函子解决方案。C++ 不支持除 const 以外的函数。您可以基于 const 重载。

于 2008-10-22T15:48:56.707 回答
-1

您可以使用模板,但是您必须在调用时指定模板参数。

于 2008-10-22T15:05:59.193 回答
-1

把它放在不同的命名空间?我会这样做。严格来说不是重载,而是只有两个具有相同名称但范围不同的方法(因此是 :: 范围解析运算符)。

所以 stringnamespace::mul 和 intnamespace::mul。也许这不是你真正要问的,但它似乎是唯一的方法。

于 2008-10-22T15:06:09.757 回答
-1

你可以做类似的事情

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

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

然后这样称呼它:

int x = mul<int>(2,3);
std::string s = mul<std::string>(2,3);

没有办法重载返回值。

于 2008-10-22T15:11:05.190 回答
-1

好的,天才们;)这就是您像专业人士一样做的事情。


class mul
{
 int m_i,m_j;
public:
 mull(int i,int j):m_i(i),m_j(j){}
 template
 operator R() 
 {
  return (R)m_i * m_j;
 }
};

使用喜欢


double d = mul(1,2);
long l = mul(1,2);

不傻<>

于 2010-05-14T05:05:37.393 回答