如何在不保存返回类型的情况下保存指向函数的指针?
例如:
int GetInt() { return 5; }
string GetStr() { return "abc"; }
FunctionPointerClass GetAny;
int main()
{
GetAny = &GetInt;
auto var = GetAny();
GetAny = &GetStr;
auto var2 = GetAny();
cout << var << '\n' << var2;
}
执行此操作的一种简单方法是使用variant<>
(感谢@sehe),如下所示:
#include <boost/variant.hpp>
#include <string>
#include <iostream>
#include <functional>
int GetInt() { return 5; }
std::string GetStr() { return "abc"; }
int main()
{
std::function<boost::variant<int, std::string>()> Get;
Get = &GetInt;
std::cout << Get() << '\n';
Get = &GetStr;
std::cout << Get() << '\n';
}
但是,它不太适用于我的项目:非类型的类。要使用它,我需要堆叠所有使用的返回类型,将其放在variant<>
的模板中。像这样:
class Var {
private:
void* _val;
template <typename T>
T& _Get() const {
return *((T*)_val);
}
// Static stack variable HERE
public:
val() {}
template <typename T>
val(T val) {
Set(val);
}
~val() {
if(_val != nullptr) delete _val;
}
std::function<boost::variant</*Stack*/>()> Get;
template <typename T>
void Set(T val) {
if(_val != nullptr) delete _val;
_val = new T(val);
Get = &_Get<T>;
// TODO Add 'T' to Stack
}
};
我该怎么做?
答案 0 :(得分:6)
不完全是。
您当然可以使其成为打印值的功能。
或者您可以使用std::variant
/ boost::variant
返回任何一种类型。
其他技术,如Type Erasure也可能适用。
我在这里充实了最后两种方法:
variant<>
#include <boost/variant.hpp>
#include <string>
#include <iostream>
#include <functional>
int GetInt() { return 5; }
std::string GetStr() { return "abc"; }
int main()
{
std::function<boost::variant<int, std::string>()> Get;
Get = &GetInt;
std::cout << Get() << '\n';
Get = &GetStr;
std::cout << Get() << '\n';
}
打印
5
abc
相关技术是类型擦除,您可以使用支持的操作(在本例中为输出流)定义“概念”,并将其隐藏在多态接口后面。 E.g:
struct Printable {
template <typename T> Printable(T v) : _stored(new concrete<T>(v)) { }
friend std::ostream& operator<<(std::ostream& os, Printable const& p) {
return p._stored->print(os);
}
private:
struct interface {
virtual std::ostream& print(std::ostream& os) const = 0;
virtual ~interface() = default;
};
template <typename T>
struct concrete : interface {
concrete(T v) : v(v) {}
virtual std::ostream& print(std::ostream& os) const override {
return os << v;
}
T v;
};
std::unique_ptr<interface> _stored;
};
在这种情况下,你可以制作整个节目:
<强> Live On Coliru 强>
int GetInt() { return 5; }
std::string GetStr() { return "abc"; }
int main()
{
std::function<Printable()> Get;
Get = &GetInt;
std::cout << Get() << '\n';
Get = &GetStr;
std::cout << Get() << '\n';
}
答案 1 :(得分:1)
我打算把它写成评论,并不是一个真正的答案,但是对于“从具有相同名称的函数返回不同类型”这一主题进行了冗长的讨论。
对于重载函数,C ++不会将返回类型转换为concideration。换句话说,std::string GetAny()
和int GetAny()
被视为同一函数的副本,因为它们仅在返回类型上有所不同。这是语言定义中的一个限制,你必须通过“做一些不同于返回两种不同类型的东西”来解决这个限制。
正如在另一个答案中所讨论的,一个解决方案是boost::variant
,这基本上是一种定义一个类的方法,该类可以在其中包含多个不同的类型,它具有一种“标记”类型来确定它真正包含的内容,以及与此相关的各种其他聪明的东西。在很多方面使它非常整洁。
然而,它仅对某些类别的问题非常有用。在许多情况下,您的代码仍然需要了解数据类型是什么,并且具有可能返回字符串,整数或任何其他“随机”数据类型的函数没有意义。是的,能够定义返回“任何”类型的函数指针是很方便的 - 但只有在你可以拥有函数表时才这样。如果您这样做,您的代码将无法正常运行:
std::string s;
s += GetAny(); // Happens to be returning `int` in this call.
同样糟糕:
int x = GetAny(); // Returning a string...
因此,虽然您可以编写可能返回“任何类型”的代码,但很难在不知道它返回的情况下使用这样的函数。我已经专业编程超过30年了,我已经使用函数指针做了很多事情。到目前为止,我已经设法不需要多次使用它了,而且每次都是类似于boost::variant
的某种形式的解决方案(基本上返回一个数据结构,其中一个字段描述了数据类型本身)。我可以想到我使用它的两个案例是在我的Lisp解释器和我的Basic intrepreter中。它们需要一个“变量类型”,它能够容纳各种类型的对象(整数,浮点数,字符串,列表[仅在Lisp中])。在我的Pascal编译器中,我确实有一个合适的类型系统,因此它不需要从单个函数(或函数指针)返回多个类型。当这种情况发生时,我会说“它闻起来很有趣”,你应该考虑一下你试图解决的问题,以及这是否是正确的解决方案。