C ++:非类型函数指针

时间:2017-09-19 22:39:48

标签: c++ function c++11 pointers

如何在不保存返回类型的情况下保存指向函数的指针?
例如:

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
    }
};

我该怎么做?

2 个答案:

答案 0 :(得分:6)

不完全是。

  • 您当然可以使其成为打印值的功能。

  • 或者您可以使用std::variant / boost::variant返回任何一种类型。

  • 其他技术,如Type Erasure也可能适用。

我在这里充实了最后两种方法:

使用variant<>

Live On Coliru

#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编译器中,我确实有一个合适的类型系统,因此它不需要从单个函数(或函数指针)返回多个类型。当这种情况发生时,我会说“它闻起来很有趣”,你应该考虑一下你试图解决的问题,以及这是否是正确的解决方案。