Naios / function2

Improved and configurable drop-in replacement to std::function that supports move only types, multiple overloads and more
http://naios.github.io/function2
Boost Software License 1.0
545 stars 46 forks source link

std::is_nothrow_move_constructible_v<fu2::unique_function<void()>> possible? #23

Closed Hans-Giebenrath closed 5 years ago

Hans-Giebenrath commented 5 years ago

@Naios

Would it be possible to support

static_assert(std::is_nothrow_move_constructible_v<fu2::unique_function<void()>>);

? I tried it with gcc (GCC) 8.2.1 20181127 and clang version 7.0.1 (tags/RELEASE_701/final) on Arch Linux. My use case is to store fu2::unique_functions in a std::vector, but upon resizing the vector, the assertion

assert(std::is_copy_constructible<T>::value && "The box is required to be copyable here!");

triggers, since the vector copies the elements to the new storage instead of moving. I don't understand the internals enough to reason about the feasability of this feature. As a workaround, preallocating enough storage in the vector works for me, as well as wrapping fu2::unique_function into a std::unique_ptr, so this feature is more a Quality Of Life improvement.

Naios commented 5 years ago

std::is_nothrow_move_constructible should be supported (in theory) if HasStrongExceptGuarantee is set. This option can be set through instantiating your own function_base object which exposes many points of configuration. The reason why this can't be supported out of the box is that std::is_nothrow_move_constructible depends on the underlying erased objects which can't be checked at compile-time and thus every objects which is stored inside the function needs to satisfy std::is_nothrow_move_constructible etc.

However in no case should the assert(std::is_copy_constructible<T>::value && "The box is required to be copyable here!"); assertion trigger. Can you provide a short minimalistic example which triggers this assertion?

Hans-Giebenrath commented 5 years ago

The assertion is triggered with all versions >= -std=c++14:

g++ -std=c++2a a.cpp && ./a.out
g++ -std=c++17 a.cpp && ./a.out
g++ -std=c++14 a.cpp && ./a.out
// file a.cpp
#include "function2.hpp"
#include <vector>

using fun_t = fu2::unique_function<int(int)>;

int main() {
    std::vector<fun_t> v;
    v.reserve(1);
    fun_t f{[](int i) { return 2 * i; }};
    fun_t f2{[](int i) { return 2 * i; }};
    v.emplace_back(std::move(f));
    v.emplace_back(std::move(f2));
    return v[0](7);
}

Thank you for your explanation, I will use HasStrongExceptGuarantee then.