At the moment I have a class defined similar to this:

class dummy{
    public:
        dummy(void(&func)(int))
            : member{func}{}

        void(&member)(int);
};

but I want to have member defined as a const function reference. I'm not sure exactly how to write this or if it is even possible.

P.S. PLEASE don't recommend me std::function I'm not oblivious to it's existence and have no objection to it, I just want to know whether something like this is doable.

有帮助吗?

解决方案

The syntax:

Syntactically, you can achieve this through a type alias (or typedef):

using function_t = void(int); // or typedef void (function_t)(int);

class dummy {
public:
    const function_t& member;
    dummy(const function_t& func)
         : member{func}{}

};

The semantics:

However, the "correct syntax" doesn't buy you anything: dummy is the same as

class dummy {
public:
    function_t& member;
    dummy(function_t& func)
         : member{func}{}

};

(which, in turn is the same as the OP's definition) because the const qualifiers are ignored as per C++11 8.3.5/6:

The effect of a cv-qualifier-seq in a function declarator is not the same as adding cv-qualification on top of the function type. In the latter case, the cv-qualifiers are ignored. [ Note: a function type that has a cv-qualifier-seq is not a cv-qualified type; there are no cv-qualified function types. —end note ]

What about rvalues?

As far as I undestand (from a comment to user657267's answer) the motivation for taking the argument as reference to const was to enable passing rvalues (temporaries):

void f(int) { }

function_t make_function() { return f; }

dummy d1(f);
dummy d2(make_function());

However, this doesn't compile, not because of dummy, but because make_function returns a function which is forbiden by C++11 8.3.5/8

If the type of a parameter includes a type of the form “pointer to array of unknown bound of T” or “reference to array of unknown bound of T,” the program is ill-formed.99 Functions shall not have a return type of type array or function, although they may have a return type of type pointer or reference to such things. There shall be no arrays of functions, although there can be arrays of pointers to functions.

A natural "solution" would be returning a reference:

function& make_function() { return f; }

or

function&& make_function() { return f; }

In both cases, the type of the expression make_function() is an lvalue (which defies the purpose of dummy using reference to const to enable passing rvalues) as per C++11 5.2.2/10

A function call is an lvalue if the result type is an lvalue reference type or an rvalue reference to function type, an xvalue if the result type is an rvalue reference to object type, and a prvalue otherwise.

Actually, value category is not an issue here. With any legal declaration of make_function and any definition of dummy seem above the declarations of d1 and d2 work fine.

Nevertheless, the mentioned comment to user657267's answer talks about passing a lambda but a reference to function cannot bind to lambda expression because it has a different type:

dummy d3([](int){}); // Error!

The suggested solution

Instead of references use pointers:

using function_t = void(int); // or typedef void (function_t)(int);

class dummy {
public:
    function_t* member;
    dummy(function_t* func)
         : member{func}{}

};

void f(int) { }

function_t& make_function() { return f; }

dummy d1(f);               // OK
dummy d2(make_function()); // OK
dummy d3([](int){});       // OK

Final remarks:

  1. Again, declaring const function_t* member and dummy(const function_t* func) doesn't buy you anything because, as per references, the const qualifiers are ignored.

  2. The initialization of d1 and d2 work because functions are implicitly converted to pointer to functions (see C++ 4.3/1).

  3. If a function argument is of function type, the compiler changes its type to a pointer to function. Hence, dummy's constructor can be declared as dummy(function_t func);.

  4. The initialization of d3 works because captureless lambdas are implicitly converted to pointer to functions. It wouldn't work for lambdas with captures.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top