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

时间:2014-01-21 10:35:26

标签: c++

我在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

4 个答案:

答案 0 :(得分:2)

A&#34;仿函数&#34; class不是函数指针。但是std::function需要构造函数,它将隐式地从函子或函数指针创建一个。

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

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

然后你可以打电话

testClass( ClassA(), name );

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

您可以为您的课程编写代理功能,这样就可以转发他们,但我不认为这是您真正想要的,因为您必须为每个代表编写一个:< / p>

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

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能够转换为函数,它不能使用验证码,但如果您需要额外的数据成员,则无法进行模板化版本。

答案 1 :(得分:1)

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

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

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

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


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

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

答案 2 :(得分:0)

testClass函数重写为函数模板。

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

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

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

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

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

答案 3 :(得分:0)

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

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版本,这是你不想考虑的更糟糕的可怕hackery。

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

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

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