У меня есть код, который в основном делает это:
struct Base {
virtual ~Base() = default;
virtual int forward() = 0;
};
struct Derived : Base {
int forward() override {
return 42;
}
};
typename std::aligned_storage<sizeof(Derived), alignof(Derived)>::type storage;
new (&storage) Derived{};
auto&& base = *reinterpret_cast<Base*>(&storage);
std::cout << base.forward() << std::endl;
Я очень сомневаюсь, что это четко определенное поведение. Если это действительно неопределенное поведение, как я могу это исправить? В коде, выполняющем reinterpret_cast
, я знаю только тип базового класса.
С другой стороны, если это четко определенное поведение во всех случаях, почему это работает и как?
Простое сохранение ссылки на содержащийся объект здесь неприменимо. В своем коде я хочу применить SBO к стираемому списку типов, где тип создается пользователем моей библиотеки и в основном расширяет класс Base
.
Я добавляю элементы внутри шаблонной функции, но в функции, которая их читает, я не могу знать тип Derived
. Вся причина, по которой я использую базовый класс, заключается в том, что мне нужна только функция forward
в моем коде, который его считывает.
Вот как выглядит мой код:
union Storage {
// not used in this example, but it is in my code
void* pointer;
template<typename T>
Storage(T t) noexcept : storage{} {
new (&storage) T{std::move(t)}
}
// This will be the only active member for this example
std::aligned_storage<16, 8> storage = {};
};
template<typename Data>
struct Base {
virtual Data forward();
};
template<typename Data, typename T>
struct Derived : Base<Data> {
Derived(T inst) noexcept : instance{std::move(inst)} {}
Data forward() override {
return instance.forward();
}
T instance;
};
template<typename> type_id(){}
using type_id_t = void(*)();
std::unordered_map<type_id_t, Storage> superList;
template<typename T>
void addToList(T type) {
using Data = decltype(type.forward());
superList.emplace(type_id<Data>, Derived<Data, T>{std::move(type)});
}
template<typename Data>
auto getForwardResult() -> Data {
auto it = superList.find(type_id<Data>);
if (it != superList.end()) {
// I expect the cast to be valid... how to do it?
return reinterpret_cast<Base<Data>*>(it->second.storage)->forward();
}
return {};
}
// These two function are in very distant parts of code.
void insert() {
struct A { int forward() { return 1; } };
struct B { float forward() { return 1.f; } };
struct C { const char* forward() { return "hello"; } };
addToList(A{});
addToList(B{});
addToList(C{});
}
void print() {
std::cout << getForwardResult<int>() << std::endl;
std::cout << getForwardResult<float>() << std::endl;
std::cout << getForwardResult<const char*>() << std::endl;
}
int main() {
insert();
print();
}
aligned_storage
. Просто укажите пользовательскийoperator new
, возвращающий выровненный фрагмент памяти и соответствующийoperator delete
для базового класса. - person Andrei R.   schedule 29.05.2017void*
. Использование нового не будет СБО. - person Guillaume Racicot   schedule 29.05.2017std::launder
в С++ 17. Очень похоже/обман: small-object-storage-strict -aliasing-rule-and-undefined-behavior. См. Ответ экатмура и ответ Якка. - person WhiZTiM   schedule 29.05.2017auto* derived = new (&storage) Derived{}; Base* base = derived;
? - person Jarod42   schedule 29.05.2017A
,B
иC
? - person Curious   schedule 30.05.2017reinterpret_cast
- person Curious   schedule 30.05.2017getForwardResult()
, чтобы она также принимала эти типы, и тогда вы можетеreinterpret_cast
к правильному производному типу таким образом - person Curious   schedule 30.05.2017forward
выполняет вычисления, определенные пользователем. - person Guillaume Racicot   schedule 30.05.2017D
, в которой есть функция переадресации, возвращающаяint
, потому что тогдаunordered_map
уже будет иметь запись для идентификатора типаint
. Так что, по сути, это отношения 1-1, а это означает, что дополнительный уровень косвенности не требуется. - person Curious   schedule 30.05.2017std::type_index
? Я никогда не пробовал это, и мне интересно :) - person Curious   schedule 30.05.2017int
,double
и тому, что вам нужно, и сделать его параметром шаблона класса шаблонаDerived
. - person Curious   schedule 30.05.2017D
, который возвращаетint
. ИспользованиеA
илиD
определяется во время выполнения. Для записи type_id может быть неопределенное количество существующих типов, но только один выбирается условием времени выполнения. - person Guillaume Racicot   schedule 30.05.2017reinterpret_cast
s этот тип к правильному производному типу, а затем возвращает этот указатель как указательBase<Data>
. По сути, вы будете делать то же самое, что и я для примера стирания текста внизу, но с другим уровнем косвенности. - person Curious   schedule 30.05.2017