我需要找到某种方法来模拟 C++ 中函数返回类型的重载。
我知道没有办法直接做到这一点,但我希望有一些开箱即用的方法。 我们正在创建一个 API 供用户使用,他们将传入一个数据字符串,该数据字符串根据字符串信息检索值。这些值是不同的类型。本质上,我们想让他们这样做:
int = RetrieveValue(dataString1);
double = RetrieveValue(dataString2);
// Obviously, since they don't know the type, they wouldn't use int =.... It would be:
AnotherFunction(RetrieveValue(dataString1)); // param of type int
AnotherFunction(RetrieveValue(dataString2)); // param of type double
但这在 C++ 中不起作用(显然)。 现在,我们正在设置它,以便他们调用:
int = RetrieveValueInt(dataString1);
double = RetrieveValueDouble(dataString2);
但是,我们不希望他们需要知道数据字符串的类型是什么。
不幸的是,我们不允许使用外部库,所以不能使用Boost。
我们有什么办法可以解决这个问题吗?
澄清一下,我知道 C++ 本身无法做到这一点。但一定有办法绕过它。例如,我考虑执行 RetrieveValue(dataString1, GetType(dataString1))。这并不能真正解决任何问题,因为 GetType 也只能有一种返回类型。但我需要这样的东西。
我知道这个问题以前曾被问过,但意义不同。我无法使用任何明显的答案。我需要一些完全开箱即用的东西才能对我有用,而其他问题中的任何答案都不是这种情况。
你必须从这个开始:
template<typename T>
T RetrieveValue(std::string key)
{
//get value and convert into T and return it
}
要支持此功能,您需要做更多工作,以便将值转换为类型
T
。转换价值的一种简单方法可能是这样的:
template<typename T>
T RetrieveValue(std::string key)
{
//get value
std::string value = get_value(key, etc);
std::stringstream ss(value);
T convertedValue;
if ( ss >> convertedValue ) return convertedValue;
else throw std::runtime_error("conversion failed");
}
请注意,您仍然必须将此函数调用为:
int x = RetrieveValue<int>(key);
如果你可以这样做的话,你可以避免两次提及
int
:
Value RetrieveValue(std::string key)
{
//get value
std::string value = get_value(key, etc);
return { value };
}
其中
Value
实现为:
struct Value
{
std::string _value;
template<typename T>
operator T() const //implicitly convert into T
{
std::stringstream ss(_value);
T convertedValue;
if ( ss >> convertedValue ) return convertedValue;
else throw std::runtime_error("conversion failed");
}
}
那么你可以这样写:
int x = RetrieveValue(key1);
double y = RetrieveValue(key2);
哪个是你想要的,对吧?
唯一明智的方法是将返回值移至参数。
void retrieve_value(std::string s, double& p);
void retrieve_value(std::string s, int& p);
<...>
double x;
retrieve_value(data_string1, x);
int y;
retrieve_value(data_string2, y);
无论是重载还是专门化,您都需要将信息包含在函数签名中。您可以将变量作为未使用的第二个参数传递:
int RetrieveValue(const std::string& s, const int&) {
return atoi(s.c_str());
}
double RetrieveValue(const std::string& s, const double&) {
return atof(s.c_str());
}
int i = RetrieveValue(dataString1, i);
double d = RetrieveValue(dataString2, d);
如果你知道你的值永远不会为零或负数,只需返回一个包含 int 和 double 的结构,并将不需要的值归零...
这是一种便宜又脏但简单的方法......
struct MyStruct{
int myInt;
double myDouble;
};
MyStruct MyFunction(){
}
如果数据字符串是编译时常量(如回答我的评论中所述),您可以使用一些模板魔法来完成这项工作。一个更简单的选择是根本不使用字符串,而是使用一些允许您重载参数的数据类型。
struct retrieve_int {} as_int;
struct retrieve_double {} as_double;
int RetrieveValue(retrieve_int) { return 3; }
double RetrieveValue(retrieve_double) { return 7.0; }
auto x = RetrieveValue(as_int); // x is int
auto y = RetrieveValue(as_double); // y is double
不幸的是,没有办法重载函数返回类型,请参阅此答案 按返回类型重载
int a=itoa(retrieveValue(dataString));
double a=ftoa(retrieveValue(dataString));
两者都返回一个字符串。
作为模板解决方案的替代方案,您可以让函数返回指向类的引用或指针,然后创建该类的子类以包含您想要返回的不同数据类型。然后
RetrieveValue
将返回对适当子类的引用。
这会让用户将返回的对象传递给其他函数,而不知道它属于哪个子类。
这种情况下的问题将成为内存管理问题之一——选择哪个函数分配返回的对象,哪个函数删除它,以及何时删除它,这样我们就可以避免内存泄漏。
答案很简单,只需声明函数返回 void* 类型,并在定义中返回对不同类型变量的引用即可。例如在标头 (.h) 中声明
void* RetrieveValue(string dataString1);
在定义(.cpp)中只需写
void* RetrieveValue(string dataString1)
{
if(dataString1.size()<9)
{
static double value1=(double)dataString1.size();
return &value1;
}
else
{
static string value2=dataString1+"some string";
return &value2;
}
}
然后在调用 RetrieveValue 的代码中将其转换为正确的值
string str;
string str_value;
double dbl_value;
if(is_string)
{
str_value=*static_cast<*string>(RetrieveValue(str));
}
else
{
dbl_value=*static_cast<*double>(RetrieveValue(str));
}
由于您使用的示例并不是您真正想要的,所以您让每个人都有点失望。
您真正拥有的设置(使用该函数的返回值来调用其返回类型未知的函数)将不起作用,因为函数调用是在编译时解析的。
然后您就只能使用运行时解决方案。我推荐访问者模式,您必须大幅更改您的设计才能实现此更改。我确实没有其他方法可以做到这一点。