4

My question concerns the line of code below marked with the // WHY??? comment.

Below is a toy example of some code I was expecting to break: a std::function variable is assigned a function pointer whose parameters almost match, but differ in const and &. It didn't break. I've figured out why it works -- there's a copy constructor call added by the compiler that I didn't expect.

My question is: why does C++ allow this case? What is the use-case that made the C++ committee decide this should work (as opposed to the compiler rejecting this assignment)?

#include <memory>
#include <functional>
#include <iostream>

class Thing {
  public:
    Thing(int count) : count_(count) {}
    int count_;
};

typedef std::function<void(const Thing&)> ConstByRefFunction;

void DoThingByValue(Thing event) { event.count_ += 5; }

int main() {
  Thing thing(95);
  ConstByRefFunction func = DoThingByValue; // WHY???
  // The following line copies thing even though anyone looking at
  // the signature of ConstByRefFunction would expect it not to copy.
  func(thing);
  std::cout << thing.count_ << std::endl; // still 95
  return 0;
}
5
  • 1
    RTM: std::function satisfies the requirements of CopyConstructible and CopyAssignable. If it stored a reference, then it would not be CopyConstructible. Commented Mar 24 at 22:08
  • 4
    @3CxEZiVlQ I beleive the OP is asking why they can assign a function that takes by value to a std::function that says it takes by const reference. Commented Mar 24 at 22:11
  • OP: If the std::function takes by const& then it shouldn't be changing the passed in parameter. I think that is why the assignment is valid since the copy follows the same semantics. Commented Mar 24 at 22:15
  • Related remark: if one delete copy constructor of Thing, compiler (at least g++) complains exactly about line with //WHY as conversion from ‘void(Thing)’ to non-scalar type ‘ConstByRefFunction’ {aka ‘std::function<void(const Thing&)>’} requested Commented Mar 24 at 22:17
  • @Alexander I discovered that. Sadly, this is for a class we want to copy, just not when it is being handled by this function pointer. It’s going to take some hijinks to get this to work the way I had hoped. Probably some sort of “I’m not copyable” wrapper type. Commented Mar 25 at 3:00

1 Answer 1

10

The signature you specify in the std::function template is the signature used for the std::function's operator() overload, NOT the signature used for the target that the operator() calls.

You can assign any target to std::function which is Callable using the parameters of the operator(). In your example, DoThingByValue() is an acceptable target because a const Thing& parameter can be passed by value to a Thing parameter (thus invoking the Thing copy constructor), no different than if you were doing something like this:

void DoThingByValue(Thing event) {
  ...
}

void DoThingByConstRef(const Thing &event) {
  DoThingByValue(event); // <-- COPY HERE
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.