繁体   English   中英

当函数的参数是函数指针时,如何将函数对象用作函数指针

[英]how can function object be used as a function pointer when the function's argument is function pointer

我有一个与C ++中的函数对象或仿函数有关的问题。 我知道在很多情况下函数对象可以替换函数指针,例如STL中的一些算法。 但是,如果函数的参数是函数指针,我不知道如何调用函数对象。 例如:

          class ClassA
        {
        public:
            void operator()(std::string &name)
            {
                std::cout<<"Class A"<<std::endl;
            }
        };

        class ClassB
        {
        public:
            void operator()(std::string &name)
            {
                std::cout<<"Class B"<<std::endl;
            }
        };

        void testClass(void(*fun)(std::string&), std::string &name  )
        {
             (*fun)(name);
        }

            void funClass(std::string &str)
            {
                std::cout<<"hello C "<<str<<std::endl;

                   }

如果我只使用函数指针,则很容易调用testClass函数:

std::string str= "hello.txt";


        testClass(funClass,str);

但是,如果我想使用函数对象,我不知道如何调用testClass。

   ClassA obj;
testClass(obj.operator()(str),str); // compilation errors

“仿函数”类不是函数指针。 但是std::function需要构造函数,它将隐式地从仿函数或函数指针创建一个。

如果允许修改testClass,那么理想情况下要使它更通用:

void testClass( std::function< void( std::string & ) > func, std::string & name )
{
   func( name );
}

然后你可以打电话

testClass( ClassA(), name );

如果它不打算修改字符串,它应该通过const引用获取此参数。

您可以为您的类编写委托函数,像这样转发它们但我不认为这是您正在寻找的,因为您必须为每个委托编写一个:

否则你可以编写这样的模板化函数

template< typename T >
void callT( std::string & name )
{
    T t;
    t( name );
}


testClass( callT< ClassA > );

如果您使用的是C++11 ,则可以在实例中传入lambda函数,该函数将转换为函数指针。 (谢谢Steve Jessop告诉我这个)。

在这种情况下,这是一个变通方法,因为您必须为要使用它的每个仿函数类型输出lambda函数。 (所以即使功能在那里模板可能更好)。

要做lambda你会做的

testClass( []( std::string & str ){ ClassA a; a(str); }, name );

请注意,对于lambda能够转换为函数,它不能使用验证码,但如果您需要额外的数据成员,则无法执行模板化版本。

类实例通常不是函数指针,而是一个不捕获任何东西的lambda类实例,它隐式转换为函数指针:

auto const f = []( std::string& s ) { classA()( s ); };
testClass( f, "blah" );

或者使用普通的命名函数。

标准库算法可以使用原始函数指针或类实例与operator() ,因为类型是模板化的。


更一般的问题是,如何将可能有状态的仿函数对象转换为原始函数指针,例如C回调,更有趣。

遗憾的是,标准库缺乏对此的支持。

testClass函数重写为函数模板。

template<typename T>
void testClass(T fun, std::string& name) {
  fun(name);
}

然后它可以接受函数指针以及函数对象的实例。

这比触发std::function机器简单。

C ++函数指针具有这种灵活性,您不必强制编写*fun()来调用指向函数。 你可以写fun()

顺便说一下,你也可以传入函数的引用。

您还可以使用线程局部堆栈的函数对象。 这就像是

template<typename Sig> struct horrible_hackery;
template<typename Ret, typename... Args> struct horrible_hackery {
    static thread_local std::stack<std::function<Ret(Args...)>> functions;
    static Ret trampoline_callback(Args... args) {
        return functions.top()(args...);
    }
    template<typename F, typename Around> static void execute_around_fptr(F f, Around a) {
        functions.push(f);
        a(&trampoline_callback);
        functions.pop();
    }
};

现在你可以使用了

ClassA obj;
horrible_hackery<void(std::string&)>::execute_around(obj, [&](void(*fptr)(std::string&)) {
    return testclass(fptr, str);
});

一个C ++ 03版的这是即使是你不想去思考更可怕可怕的两轮牛车。

如果你真的非常渴望这个功能,并且你不能接受上面纯粹的基于堆栈的解决方案,你可以为你选择的平台下载一个JIT编译器,如LLVM,然后为自己JIT一个thunk。 这非常极端。

然而,这些限制和通常可怕的hackery导致以下结论:

  • 如果可能,首选模板化的函数对象类型。
  • 如果不可能,请使用std :: function以最佳方式处理此问题。
  • 不要使用函数指针或它们甚至更邪恶的兄弟姐妹,成员函数指针,除非你有一些具体的要求,比如躲避ABI子弹。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM