Why doesn't the C++ standard library std::function just give error that target is not copy-constructible only IF it&

So this question explains why it's required that the target of a std::function requires that a val

So this question explains why it's required that the target of a std::function requires that a valid copy constructor be callable. But the thing is, I know with the compilers I've dealt with that it's not an error to not have a constructor if it's not called. So if I have a class type with a deleted copy constructor and all I do in my code is move it around, then that shouldn't be an error. I have a std::function that captures my own class type with a deleted copy constructor, and all I do is 'move' the std::function, which should compile ordinarily. But the error I get is:

static_assert failed: 'The target function object type must be copy constructible (N4988 [func.wrap.func.con]/10.1).'

So it seems that it checks if it's copy-constructible even though no copy-construction will be done. Couldn't they have done that check/static_assert within the copy-constructor, so that when you try to copy-construct it, THEN you get the error. It just seems way less flexible like this.

So this question explains why it's required that the target of a std::function requires that a valid copy constructor be callable. But the thing is, I know with the compilers I've dealt with that it's not an error to not have a constructor if it's not called. So if I have a class type with a deleted copy constructor and all I do in my code is move it around, then that shouldn't be an error. I have a std::function that captures my own class type with a deleted copy constructor, and all I do is 'move' the std::function, which should compile ordinarily. But the error I get is:

static_assert failed: 'The target function object type must be copy constructible (N4988 [func.wrap.func.con]/10.1).'

So it seems that it checks if it's copy-constructible even though no copy-construction will be done. Couldn't they have done that check/static_assert within the copy-constructor, so that when you try to copy-construct it, THEN you get the error. It just seems way less flexible like this.

Share Improve this question asked Mar 22 at 23:39 ZebrafishZebrafish 15k3 gold badges66 silver badges152 bronze badges 0
Add a comment  | 

3 Answers 3

Reset to default 9

But the thing is, I know with the compilers I've dealt with that it's not an error to not have a constructor if it's not called.

In template code; vector<move_only> is able to do that. But this is because the vector type is a template on the move_only type. It can do compile-time stuff to only generate a copy constructor if move_only is copyable. Why?

Because "it" is vector<move_only>. The compiler sees the template and its parameters, and instantiates them all at the same time.

While function is a template, the template parameter is not the non-copyable type. The parameter is a function signature. function<void()> can be given any object type which is callable with no parameters... and is copy-constructible.

The mechanism by which it is possible for a function to store a multitude of possible objects without those object types being part of the function's template parameters is called "type-erasure". One common way to achieve type erasure is to use an internal base class that provides prototypes for the core functionality being erased. The derived class is itself a template on the type being erased. That way, only function's constructor needs to know the type of the function being erased; all of its other operations just use the base class.

Including the copy constructor. Because the copy constructor cannot know the type of the object being copied, it must rely on the base class's functionality. And that base class doesn't know the type either, so it must call a virtual function to copy the erased object.

And that virtual function must exist. The derived class must override it. Even though the derived class is a template on the erased type, the base class doesn't know anything about it. So the derived class must provide that functionality... or error out. By instantiating the derived class at all, the functionality is required to exist.

Pretty much every type-erasure mechanism runs into this problem, one way or another. Type erasure cannot work with the kind of template methods you're talking about.

std::function is a type-erasing wrapper, so as your mental model you can consider it has a member of function pointer type to copy the callable. This function pointer must be initialized when the std::function is initialized or assigned.

Could this be made not an error? Yes. You could point it at a function which throws an exception instead.

Could there be a compile error instead? No. At the point where the copy constructor is called, all we know is that there is a function we can call.

The trade-off is thus: having a runtime error or not.

all I do in my code is move it around

But the compiler can't prove that. Suppose

struct S {
    S() {}
    S(const S&) = delete;
    S(S&&) = default;
    int operator()() { return 42; }
};

std::function<int()> foo() {
    return S{}; // 1
}

std::function<int()> bar() {
    return []{ return 420; }; // 2
}

auto baz(bool b) {
    auto f = b ? foo() : bar();
    return std::pair{f, f};
}

How can the compiler prove at 1 that no copies of std::function holding S will ever take place while simultaneously allow std::function created at 2 to be copyable?

In the case where you don't compile the entire program at once, it's literally impossible.

In the case where you do, C++ doesn't let you express ”I swear this is the entire program, please prove nontrivial properties about the program and emit an error if they don't hold", which is undecidable in general anyways.

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1744300930a4567495.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信