How does wrapping a function pointer and function

2019-06-05 17:04发布

The following template definition

template <typename Func, typename ReturnType, typename... Arguments>
class Command
{
public:
    Command(Func f) : m_func(f) { }
    ReturnType operator()(Arguments... funcArgs) { return m_func(funcArgs...); }
private:
    Func m_func;
};

gives an error message with gcc 4.7.3 (error: field 'Command::m_func' invalidly declared function type) when instantiated with the following test code:

void testFunction(int i, double d)
{
    std::cout << "TestFunctor::operator()(" << i << ", " << d << ") called." << std::endl;
}

int main()
{
    void (&fRef)(int, double) = TestFunction;
    Command<void(int, double), void, int, double> testCommand(fRef);
}

The error message also occurs if I pass TestFunction without the address-of operator into the testCommand constructor, but disappears if I pass either an explicitly named function pointer or use the address-of operator to pass the parameter. I'm under the impression that this code should work given Chapter 5 of Modern C++ Design.

What is the reasoning behind not being able to store a reference to a function, but function pointers work fine? Are there any workarounds that would allow this to compile without losing support for being able to pass functors as arguments to Command's constructor as well?

2条回答
闹够了就滚
2楼-- · 2019-06-05 17:51

Changing one line could fix it:

Command<void(*)(int, double), void, int, double> testCommand(fRef);

The difference is, you're passing a function pointer now, instead of a function type. (Functions aren't copyable, but pointers are).

The reference fRef decays to a function pointer when you pass it.

I wouldn't suggest using std::function if performance mattered.

See it live on Coliru

Note that with a little rewriting, you can make it all work much nicer:

int main()
{
    auto command = make_command(testFunction);
    command(1, 3.14);
}

To do this, I'd suggest changing the Command template to be:

template <typename Func>
class Command
{
    Func m_func;
public:
    Command(Func f) : m_func(f) { }

    template <typename... A> auto operator()(A... args) const 
        -> decltype(m_func(args...)) 
    { return m_func(args...); }
};

And now you can have type-deduction on the Func template parameter by having a factory function:

template <typename Func> Command<Func> make_command(Func f)
{
    return Command<Func>(f);
}

See this approach live on Coliru too. Of course, the output it the same:

TestFunctor::operator()(1, 3.14) called.
查看更多
小情绪 Triste *
3楼-- · 2019-06-05 17:56

C++11 offers an std::function template. You don't have to mess with function pointers.

You can pass those by reference, copy them, move them and they can even be used to store lambdas:

std::function<void()> func = []() { std::cout << "Hi" << std::endl; };
查看更多
登录 后发表回答