При каких обстоятельствах стандарт требует, чтобы T
имеет точно такой же размер и выравнивание, как std::aligned_storage<sizeof(T), alignof(T)>
?
Я думал, что ответ был всегда, но от другие SO ответы сегодня, Я узнал, что для некоторых неизвестных T
, это может быть определено реализацией. Если реализация поддерживает расширенные типы выравнивания, такие как 128-битные целые числа, то до реализации std::aligned_storage
в итоге получится 128-битное выравнивание, если я правильно понял.
Есть ли другие типы T
где ответ на вопрос может быть определен реализацией?
В моем приложении у меня в основном есть кортеж std::tuple<T, U, V>
неизвестных типов, и я хочу быть в состоянии получить смещения членов T
, U
, V
в этом кортеже максимально эффективно, в идеале во время компиляции.
(Я понимаю, что это очень странная вещь. Я пытаюсь исправить существующий код, в котором кортеж накладывается поверх «совместимого с макетом» типа, используя reinterpret_cast
, Такое приведение между несвязанными объектами является незаконным и нарушает строгие правила наложения имен. Если вместо этого я смогу получить смещения участников, то смогу отсеять нелегальный состав.)
AFAIK, это не может быть сделано в C ++ 14 constexpr
, Но это может быть довольно близко, мой код выглядит так:
#include <type_traits>
#include <tuple>
#include <utility>
#include <memory>
template <typename T>
struct tuple_of_aligned_storage;
template <typename... Ts>
struct tuple_of_aligned_storage<std::tuple<Ts...>> {
using type = std::tuple<std::aligned_storage_t<sizeof(Ts), alignof(Ts)>...>;
};
template <typename T>
using tuple_of_aligned_storage_t = typename tuple_of_aligned_storage<T>::type;
template <typename S>
struct offset_helper {
// Get offset of idx'th member
template <std::size_t idx>
static std::ptrdiff_t offset() {
constexpr tuple_of_aligned_storage_t<S> layout{};
// TODO: Do modern compilers optimize `layout` object out of the binary?
// If so, this call is probably inlined.
return reinterpret_cast<const char *>(&std::get<idx>(layout)) - reinterpret_cast<const char *>(&layout);
}
};
int main() {
using T = std::tuple<int, float, double>;
return offset_helper<T>::offset<0>(); // offset of 0'th member
}
В экспериментах большинство современных компиляторов оптимизируют это до постоянной величины, даже несмотря на то, что при этом используются переинтерпретации и тому подобное, и offset
функция не может быть отмечена constexpr
,
Точка использования std::aligned_storage
здесь, даже если члены кортежа не являются конструктивными по умолчанию, имеют нетривиальную инициализацию и т. д., заменяя их на std::aligned_storage
делает их тривиально конструктивными без изменения макета кортежа.
Но, если для некоторых типов, поменять тип на std::aligned_storage
изменяет размер или выравнивание, что может нарушить весь мой подход и привести к неправильным расчетам смещений.
Задача ещё не решена.
Других решений пока нет …