草庐IT

c++ - 将任意类的成员函数指针存储为类实例变量

coder 2024-02-22 原文

SO 上有几个问题解决了将函数指针作为参数/实参传递的问题(hereherehere 等)。其实我问了一个related question另一天。但是,这个问题有点不同。

我的问题是我正在编写一个非常灵活的类。

我现在拥有的适用于非成员函数。下面贴出来

template <typename T>
class MyClass
{
    private:
        typedef double (*firstFunctionPtr) (const T &var);

        typedef bool (*secondFunctionPtr)(const T &var);

        // Function pointers as member variables
        firstFunctionPtr _firstFunc;
        secondFunctionPtr _secondFunc;

    public:
        inline MyClass(firstFunctionPtr firstFunc, 
            secondFunctionPtr secondFunc);
};

template<typename T>
MyClass<T>::MyClass(firstFunctionPtr firstFunc, secondFunctionPtr secondFunc) :
    _firstFunc(firstFunc),
    _secondFunc(secondFunc),
    {}

但是,当我需要使用指向某个其他任意类的成员函数的指针进行初始化时,这种方法就会崩溃,不幸的是,对于我来说,这恰好是我的目的的常见用例。

This answer建议

In a proper C++ interface you might want to have a look at having your function take templated argument for function objects to use arbitrary class types.

但是,我无法编译。我试过模板化我的 typedef(使用 C++11 别名方法),并且我试过向类添加第二个模板参数来处理那些成员函数的调用类,但是这两种方法都没有用。

This Q/A似乎正在朝着我想要做的事情发展,但我无法弄清楚它的正面或反面。

  1. 有人可以解释我如何修改我的类以处理传入的任意成员函数指针吗?
  2. 此外,是否可以让它既可以处理任意成员函数,也可以处理非成员函数?
  3. 最后,是否可以使用模板来做到这一点?

郑重声明,我试图避免使用 functional header ,但不使用它可能是愚蠢的差事。

最佳答案

如果你想要MyClass成为一个可以同时拥有自由功能的模板 类型指针:

double (*)(const T &var);
bool (*)(const T &var);

对于某些参数类型 T , 或者成员函数 类型指针:

double (C::*)(const T &var);
bool (C::*)(const T &var);

对于某些参数类型 CT然后,MyClass必须参数化 由两者TC并且您需要两个专业:

  1. 在哪里C是一些非类类型
  2. 在哪里C是任何类类型

在情况(1)中,非类类型 C不可能有成员函数, 以便实现自由函数指针特化。

在情况 (2) 中,类 C可以是一个有成员函数的,这样一个 将实现成员函数指针特化。

非类类型的明显选择 Cvoid .所以我们可以制作C 默认为 void :

主模板

template<typename T, typename C = void>
struct MyClass;

这样:

MyClass<T>

将是 T 的自由函数指针特化, 和:

MyClass<T,C>

对于任何C除了void , 将是成员函数指针特化。

如您所知,您可以使用 std::enable_if SFINAE 使编译器 选择类模板的一种特化或另一种特化,取决于是否 它的模板参数 U满足一些编译时测试。你可以拿 这里的方法,但另一种不需要该设备的方法可用:

从主模板开始,我们只想拥有:

免费函数特化

template<typename T>
struct MyClass<T>
{
    ... for free function pointers ...
};

和:

成员函数特化

template<typename T, typename C>
struct MyClass<T,C>
{
    ... for member function pointers ...
};

但我们不能只拥有它,因为成员函数“specialization”恰好有 与主模板相同的模板参数。这意味着它不是 特化,编译器不允许。

但是,您可以轻松地解决这个问题,只需提供主要的 template 一个它不需要的默认模板参数,但是它的 存在使这两个专业都能站稳脚跟。

新主模板

template <typename T, typename C = void, typename Default = void> 
struct MyClass;

所以这是一个说明性的解决方案:

// Primary template
template <typename T, typename C = void, typename Default = void> 
struct MyClass;

// Free function specialization
template <typename T>
struct MyClass<T>
{
    using firstFunctor_t = double(*)(T const &);
    using secondFunctor_t = bool(*)(T const &);

    MyClass(firstFunctor_t firstFunc, secondFunctor_t secondFunc)
    :   _firstFunc(firstFunc),
        _secondFunc(secondFunc)
    {}

    double callFirst(T const & var) {
        return _firstFunc(var);
    }

    bool callSecond(T const & var) {
        return _secondFunc(var);
    }

private:

    firstFunctor_t _firstFunc;
    secondFunctor_t _secondFunc;
};

// Member function specialization
template <typename T, typename C>
struct MyClass<T,C>
{
    using firstFunctor_t = double(C::*)(T const &);
    using secondFunctor_t = bool(C::*)(T const &) const;

    MyClass(firstFunctor_t firstFunc, secondFunctor_t secondFunc)
    :   _firstFunc(firstFunc),
        _secondFunc(secondFunc)
    {}

    double callFirst(C & obj, T const & var) {
        return (obj.*_firstFunc)(var);
    }

    double callFirst(C const & obj, T const & var) {
        auto & o = const_cast<C&>(obj);
        return (o.*_firstFunc)(var);
    }

    bool callSecond(C & obj, T const & var) {
        return (obj.*_secondFunc)(var);
    }

    bool callSecond(C const & obj, T const & var) {
        auto & o = const_cast<C&>(obj);
        return (o.*_secondFunc)(var);
    }

private:

    firstFunctor_t _firstFunc;
    secondFunctor_t _secondFunc;
};

在成员函数特化中,注意两点你可能 没有考虑过:-

我决定要存储的第二个成员函数应该是 const 成员函数。 C 的成员函数很可能是 需要 T const &参数和返回 bool 将是 const成员 功能,不是吗?如果是这样,那么const-ness必须是的一部分 我在特化中使用的成员函数类型定义:

using secondFunctor_t = bool(C::*)(T const &) const;

或尝试用任何 bool (C::*)(T const &) const 实例化特化 将无法编译。

另外,我为每个 MyClass<T,C>::callFirst 提供了两个重载 和 MyClass<T,C>::callSecond , 一个带参数:

C & obj, T const & var

还有一个带参数的:

C const & obj, T const & var

没有第二个,尝试调用 MyClass<T,C>::callFirstMyClass<T,C>::callSecondobj那是 const 将失败 编译。

对于演示此解决方案的程序,您可以附加:

#include <iostream>
#include <string>

double foo(std::string const & s)
{
    return std::stod(s);
}

bool bar(std::string const & s)
{
    return s.size() > 0;
}

struct SomeClass
{
    SomeClass(){};
    double foo(std::string const & s) {
        return ::foo(s);
    }

    bool bar(std::string const & s) const {
        return ::bar(s);
    }
};

int main()
{
    MyClass<std::string> my0{foo,bar};
    std::cout << std::boolalpha;
    std::cout << my0.callFirst("1.11") << std::endl;
    std::cout << my0.callSecond("Hello World") << std::endl;

    MyClass<std::string,SomeClass> my1{&SomeClass::foo,&SomeClass::bar};
    SomeClass thing;
    std::cout << my1.callFirst(thing,"2.22") << std::endl;
    std::cout << my1.callSecond(thing,"Hello World") << std::endl;

    SomeClass const constThing;
    std::cout << my1.callFirst(constThing,"3.33") << std::endl;
    std::cout << my1.callSecond(constThing,"Hello World") << std::endl;
    return 0;
}

See it live

您说您希望此模板“极其灵活”。这 图示解决方案适合您的示例,但您可能 有兴趣知道它几乎没有您所能得到的那么灵活。 对于自由函数和成员函数,附加 variadic template 参数,您的模板可以存储和调用 [member] 函数 任意返回类型和任意类型参数的任意数量。 参见 this question和 回答。

关于c++ - 将任意类的成员函数指针存储为类实例变量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42182980/

有关c++ - 将任意类的成员函数指针存储为类实例变量的更多相关文章

  1. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

    总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

  2. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  3. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  4. ruby-on-rails - 如何使用 instance_variable_set 正确设置实例变量? - 2

    我正在查看instance_variable_set的文档并看到给出的示例代码是这样做的:obj.instance_variable_set(:@instnc_var,"valuefortheinstancevariable")然后允许您在类的任何实例方法中以@instnc_var的形式访问该变量。我想知道为什么在@instnc_var之前需要一个冒号:。冒号有什么作用? 最佳答案 我的第一直觉是告诉你不要使用instance_variable_set除非你真的知道你用它做什么。它本质上是一种元编程工具或绕过实例变量可见性的黑客攻击

  5. ruby 正则表达式 - 如何替换字符串中匹配项的第 n 个实例 - 2

    在我的应用程序中,我需要能够找到所有数字子字符串,然后扫描每个子字符串,找到第一个匹配范围(例如5到15之间)的子字符串,并将该实例替换为另一个字符串“X”。我的测试字符串s="1foo100bar10gee1"我的初始模式是1个或多个数字的任何字符串,例如,re=Regexp.new(/\d+/)matches=s.scan(re)给出["1","100","10","1"]如果我想用“X”替换第N个匹配项,并且只替换第N个匹配项,我该怎么做?例如,如果我想替换第三个匹配项“10”(匹配项[2]),我不能只说s[matches[2]]="X"因为它做了两次替换“1fooX0barXg

  6. ruby - 通过 ruby​​ 进程共享变量 - 2

    我正在编写一个gem,我必须在其中fork两个启动两个webrick服务器的进程。我想通过基类的类方法启动这个服务器,因为应该只有这两个服务器在运行,而不是多个。在运行时,我想调用这两个服务器上的一些方法来更改变量。我的问题是,我无法通过基类的类方法访问fork的实例变量。此外,我不能在我的基类中使用线程,因为在幕后我正在使用另一个不是线程安全的库。所以我必须将每个服务器派生到它自己的进程。我用类变量试过了,比如@@server。但是当我试图通过基类访问这个变量时,它是nil。我读到在Ruby中不可能在分支之间共享类变量,对吗?那么,还有其他解决办法吗?我考虑过使用单例,但我不确定这是

  7. ruby - 在没有 sass 引擎的情况下使用 sass 颜色函数 - 2

    我想在一个没有Sass引擎的类中使用Sass颜色函数。我已经在项目中使用了sassgem,所以我认为搭载会像以下一样简单:classRectangleincludeSass::Script::FunctionsdefcolorSass::Script::Color.new([0x82,0x39,0x06])enddefrender#hamlengineexecutedwithcontextofself#sothatwithintemlateicouldcall#%stop{offset:'0%',stop:{color:lighten(color)}}endend更新:参见上面的#re

  8. ruby-on-rails - 如何在我的 Rails 应用程序 View 中打印 ruby​​ 变量的内容? - 2

    我是一个Rails初学者,但我想从我的RailsView(html.haml文件)中查看Ruby变量的内容。我试图在ruby​​中打印出变量(认为它会在终端中出现),但没有得到任何结果。有什么建议吗?我知道Rails调试器,但更喜欢使用inspect来打印我的变量。 最佳答案 您可以在View中使用puts方法将信息输出到服务器控制台。您应该能够在View中的任何位置使用Haml执行以下操作:-puts@my_variable.inspect 关于ruby-on-rails-如何在我的R

  9. ruby-on-rails - 在 ruby​​ 中使用 gsub 函数替换单词 - 2

    我正在尝试用ruby​​中的gsub函数替换字符串中的某些单词,但有时效果很好,在某些情况下会出现此错误?这种格式有什么问题吗NoMethodError(undefinedmethod`gsub!'fornil:NilClass):模型.rbclassTest"replacethisID1",WAY=>"replacethisID2andID3",DELTA=>"replacethisID4"}end另一个模型.rbclassCheck 最佳答案 啊,我找到了!gsub!是一个非常奇怪的方法。首先,它替换了字符串,所以它实际上修改了

  10. ruby-on-rails - Rails 模型——非持久类成员或属性? - 2

    对于Rails模型,是否可以/建议让一个类的成员不持久保存到数据库中?我想将用户最后选择的类型存储在session变量中。由于我无法从我的模型中设置session变量,我想将值存储在一个“虚拟”类成员中,该成员只是将值传递回Controller。你能有这样的类(class)成员吗? 最佳答案 将非持久属性添加到Rails模型就像任何其他Ruby类一样:classUser扩展解释:在Ruby中,所有实例变量都是私有(private)的,不需要在赋值前定义。attr_accessor创建一个setter和getter方法:classUs

随机推荐