Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does C++23 std::move_only_function not have deduction guides?

C++23 introduced std::function's cousin std::move_only_function, just like its name, it is a move-only wrapper for move-only callable objects (demo):

#include <functional>
#include <memory>

int main() {
  auto l = [p = std::make_unique<int>(0)] { };
  std::function<void(void)>           f1{std::move(l)}; // ill-formed
  std::move_only_function<void(void)> f2{std::move(l)}; // well-formed
}

But unlike std::function, the standard does not define deduction guides for it (demo):

#include <functional>

int func(double) { return 0; }
int main() {
  std::function f1{func};           // guide deduces function<int(double)>
  std::move_only_function f2{func}; // deduction failed
}

Is there a reason for banning CTAD?

like image 919
康桓瑋 Avatar asked Oct 09 '21 17:10

康桓瑋


People also ask

What is the purpose of std :: in C ++?

Instances of std::function can store, copy, and invoke any CopyConstructible Callable target -- functions, lambda expressions, bind expressions, or other function objects, as well as pointers to member functions and pointers to data members.

Why did we need to use an std :: function object?

It lets you store function pointers, lambdas, or classes with operator() . It will do conversion of compatible types (so std::function<double(double)> will take int(int) callable things) but that is secondary to its primary purpose.

Is STD function a pointer?

No. One is a function pointer; the other is an object that serves as a wrapper around a function pointer. They pretty much represent the same thing, but std::function is far more powerful, allowing you to do make bindings and whatnot.

Can std :: function be moved?

Yes, unless you catch it, or unless you mark the destructor noexcept(false) . And in the latter case you have to be pretty careful or it may result in std::terminate() anyway.

What does the function move do in C++?

The standard C++ library gained a function template called std::move, which, despite its name, does not move anything. std::move merely casts its argument to an rvalue reference to allow moving it, but doesn't guarantee a move operation. For example, we can write a more effective version of swap using std::move :

Is there a move-only form of function in c++20?

It included multiple references to implementations of move-only functions and made a strong case for the importance of a move-only form of std::function . Feebdack given was encouragement for targetting C++20. An updated version of that paper was presented on Saturday at the end of the San Diego meeting.

What happens if a move_only_function contains no target?

If a std::move_only_function contains no target, it is called empty. Unlike std::function, invoking an empty std::move_only_function results in undefined behavior. std::move_only_functions supports every possible combination of cv-qualifiers, ref-qualifiers, and noexcept-specifiers not including volatile provided in its template parameter.

When should you not use std move?

However, std::move must be used judiciously; using it blithely may lead to performance degradation, or simply be redundant, affecting readability of the code. Fortunately, the compiler can sometimes help with finding such wrong uses of std::move.


1 Answers

Type-erasing wrappers like move_only_function are designed to be used on API boundaries, where the types are explicit, which makes CTAD for these of dubious usefulness.

Any CTAD for these callable wrappers would have to be quite limited anyway - it can't handle overloaded functions or function templates, which also means that it can't handle generic lambdas, which is a rather significant limitation on its usefulness. std::function's CTAD also comes with a disclaimer that later standards can change the deduced type (we haven't changed it yet, but we haven't removed the disclaimer either).

And with move_only_function it's not just the return and argument types that are deduced. const, noexcept, and ref-qualifiers are all in play, and that introduces new design issues. For instance, does deducing from int (*)(int) deduce int(int)? Why not int(int) const - function pointers are const-callable, after all?

And if CTAD turns out to be needed - and someone come up with a good design for it - it can always be added later.

I don't know if these points were all raised in the LEWG discussion (the minutes are rather sparse), but I think they are more than enough to justify not supporting CTAD.

like image 101
T.C. Avatar answered Oct 27 '22 12:10

T.C.