根据返回值重载一个C++函数
Posted
技术标签:
【中文标题】根据返回值重载一个C++函数【英文标题】:Overload a C++ function according to the return value 【发布时间】:2010-09-18 13:49:02 【问题描述】:我们都知道可以根据参数重载一个函数:
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之间,无需验证输入或进行任何错误处理。
【问题讨论】:
嗯,以下code project article 似乎可以满足您的需求。一定是魔法;) 我希望这是可能的。我曾遇到过几个本可以使用它来简化代码的情况...... @Brian - 效果是可能的。请参阅下面的答案。 但是仅仅因为你可以......并不意味着你应该。 :) 很高兴给某人带来困难...... 这里只发生了隐式转换,没有发生重载 【参考方案1】:你必须告诉编译器使用哪个版本。在 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
【讨论】:
我很伤心。从好的方面来说,它既聪明又准确。不利的一面是,它是一把上膛的枪。 @jrodman : 请您详细说明一下“上膛枪”的技术输入,好吗?我看不出我在这个答案中写的任何内容(虚拟指针或模板化返回值)是如何反对语言的。 @jrodman:C++ 本身就是一把上膛的枪。这就是我们如此热爱它的原因:)【参考方案2】: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;
;
我不会用那个。
【讨论】:
您可以添加转换函数,如operator identity<int(*)()>::type() return &operator int;
,它允许将其传递给采用int(*)()
的函数,如果调用函数指针,则调用operator int
函数。【参考方案3】:
如果你想让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
作为函数传递给标准算法:
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;
【讨论】:
【参考方案4】:没有。
你不能通过返回值重载,因为调用者可以用它做任何事情(或什么都不做)。考虑:
mul(1, 2);
返回值只是被扔掉了,所以不可能仅仅根据返回值来选择重载。
【讨论】:
事实上你可以这样做,当返回值被忽略时,不需要做任何工作。【参考方案5】:在中间类中使用隐式转换。
class BadIdea
public:
operator string() return "silly";
operator int() return 15;
;
BadIdea mul(int, int)
你明白了,不过这个想法很糟糕。
【讨论】:
【参考方案6】:设 mul 是一个类,mul(x, y) 是它的构造函数,并重载一些强制转换运算符。
【讨论】:
【参考方案7】:您不能仅根据返回值重载函数。
但是,虽然严格来说这不是一个重载函数,但您可以从函数中返回一个重载转换运算符的类的实例。
【讨论】:
【参考方案8】:我想你可以让它返回一些奇怪的类型 Foo ,它只捕获参数,然后 Foo 有一个隐式运算符 int 和运算符 string,它会“工作”,尽管它不会真正重载,而是一个隐式转换技巧。
【讨论】:
【参考方案9】:简而言之,答案是否定的。在 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);
【讨论】:
【参考方案10】:据我所知,你不能(可惜……)。作为一种解决方法,您可以改为定义一个“out”参数,然后重载该参数。
【讨论】:
【参考方案11】:不在 C++ 中。你在上面的例子中得到的是返回值,它是一个 int 转换成 string
可以理解的东西,很可能是 char
。这将是 ASCII 18 或“设备控制 2”。
【讨论】:
【参考方案12】:您可以使用上面的仿函数解决方案。 C++ 不支持除 const 以外的函数。您可以基于 const 重载。
【讨论】:
【参考方案13】:您可以使用模板,但您必须在调用时指定模板参数。
【讨论】:
【参考方案14】:把它放在不同的命名空间?我会这样做。严格来说不是重载,而是只有两个具有相同名称但范围不同的方法(因此是 :: 范围解析运算符)。
所以 stringnamespace::mul 和 intnamespace::mul。也许它不是你真正想要的,但它似乎是唯一的方法。
【讨论】:
【参考方案15】:你可以做类似的事情
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);
没有办法重载返回值。
【讨论】:
【参考方案16】:好的,天才们;)这就是你像专业人士一样做的事情。
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);
不傻
【讨论】:
以上是关于根据返回值重载一个C++函数的主要内容,如果未能解决你的问题,请参考以下文章