Ответ 1
Нет, это не безопасно. Если компилятор говорит, что что-то не может быть тривиально скопировано, это не может быть.
Он может работать. Но это работает не означает, что это безопасно.
Даже если он работает сегодня, но завтра он перестает работать после обновления компилятора.
Исправление довольно простое. Напишите тип SBO (оптимизация небольшого буфера), который не требует тривиально гибкого использования.
template<std::size_t S, std::size_t A>
struct SBO {
void(*destroy)(SBO*) = nullptr;
// void(*copy_ctor)(SBO const* src, SBO* dest) = nullptr;
void(*move_ctor)(SBO* src, SBO* dest) = nullptr;
std::aligned_storage_t< S, A > buffer;
void clear() {
auto d = destroy;
destroy = nullptr;
// copy_ctor = nullptr;
move_ctor = nullptr;
if (d) d(this);
}
template<class T, class...Args>
T* emplace( Args&&... args ) {
static_assert( sizeof(T) <= S && alignof(T) <= A, "not enough space or alignment" );
T* r = new( (void*)&buffer ) T(std::forward<Args>(args)...);
destroy = [](SBO* buffer) {
((T*)&buffer->buffer)->~T();
};
// do you need a copy ctor? If not, don't include this:
//copy_ctor = [](SBO const* src, SBO* dest) {
// auto s = (T const*)&src.buffer;
// dest->clear();
// dest->emplace<T>( *s );
//};
move_ctor = [](SBO* src, SBO* dest) {
auto* s = (T*)&src->buffer;
dest->clear();
dest->emplace<T>( std::move(*s) );
src->clear();
};
return r;
}
SBO() = default;
SBO(SBO&& o) {
if (o.move_ctor) {
o.move_ctor(&o, this);
}
}
SBO& operator=(SBO&& o) {
if (this == &o) return *this; // self assign clear, which seems surprising
if (o.move_ctor) {
o.move_ctor(&o, this);
}
return *this;
}
// do you need a copy ctor? If so, implement `SBO const&` ctor/assign
};
Теперь вот пинчлин. std::function
почти наверняка уже делает это для вас.
Поместите небольшой тип с перемещением без броска и создайте в std::function
и спросите, может ли создание метать. Я предполагаю, что ваша реализация будет использовать SBO для хранения типа там.
MSVC 2015 Я думаю, хватит места для лямбда, хранящей два std::string
s.
Накладные расходы, чтобы сделать все правильно, являются скромными (два указателя и небольшая косвенность). Вы можете снизить стоимость хранения до одного указателя на один экземпляр за счет большей косвенности (привяжите таблицу к "ручной vtable", хранящейся как статический локальный объект в функции factory: я могу предоставить ссылку на примеры, если это не так 't зажгите лампочку), но с помощью двух стираемых методов также можно сохранить их локально (при 3+ рассмотрите статическую таблицу), если пространство не стоит на высоте.
Вы уже вызываете "стирание", что в основном требует хранения указателя на функцию, добавление перемещения (и, возможно, копирование) и уничтожение - это не намного больше накладных расходов.