Мне было интересно, как сделать макет памяти класса более эффективным в шаблоном. Насколько мне известно, Стандарт обязывает членов данных класса, которые будут изложены в памяти по порядку их объявления. Может быть возможным дополнение, выполняемое компилятором, чтобы выровнять элементы данных, добавляя излишне к размеру класса. Идея состоит в том, чтобы реорганизовать объявления элементов данных во время компиляции, чтобы свести к минимуму такие дополнения. Я сделал некоторые поиски, но не смог найти никакой информации (большую часть времени люди обсуждают директивы компилятора упаковки, что не совсем то же, что и я).Повторная компоновка элементов данных?
Во-первых, пожалуйста, рассмотрим следующую (тривиальное, но повторяющиеся и безобразно) код (same code on ideone.com) (вопросы находятся ниже кода, не стесняйтесь, чтобы пропустить прямо к ним): выход
#include <iostream>
#include <cstdint>
namespace so
{
template <typename Ta, typename Tb, typename Tc, std::size_t =
((sizeof(Ta) >= sizeof(Tb)) && (sizeof(Tb) >= sizeof(Tc))) ? 10 :
((sizeof(Ta) >= sizeof(Tc)) && (sizeof(Tc) >= sizeof(Tb))) ? 11 :
((sizeof(Tb) >= sizeof(Ta)) && (sizeof(Ta) >= sizeof(Tc))) ? 20 :
((sizeof(Tb) >= sizeof(Tc)) && (sizeof(Tc) >= sizeof(Ta))) ? 21 :
((sizeof(Tc) >= sizeof(Ta)) && (sizeof(Ta) >= sizeof(Tb))) ? 30 :
((sizeof(Tc) >= sizeof(Tb)) && (sizeof(Tb) >= sizeof(Ta))) ? 31 : 0>
struct foo {};
template <typename Ta, typename Tb, typename Tc>
struct foo<Ta, Tb, Tc, 10>
{
Ta a;
Tb b;
Tc c;
foo(Ta _a, Tb _b, Tc _c) : a{_a}, b{_b}, c{_c} {}
};
template <typename Ta, typename Tb, typename Tc>
struct foo<Ta, Tb, Tc, 11>
{
Ta a;
Tc c;
Tb b;
foo(Ta _a, Tb _b, Tc _c) : a{_a}, c{_c}, b{_b} {}
};
template <typename Ta, typename Tb, typename Tc>
struct foo<Ta, Tb, Tc, 20>
{
Tb b;
Ta a;
Tc c;
foo(Ta _a, Tb _b, Tc _c) : b{_b}, a{_a}, c{_c} {}
};
template <typename Ta, typename Tb, typename Tc>
struct foo<Ta, Tb, Tc, 21>
{
Tb b;
Tc c;
Ta a;
foo(Ta _a, Tb _b, Tc _c) : b{_b}, c{_c}, a{_a} {}
};
template <typename Ta, typename Tb, typename Tc>
struct foo<Ta, Tb, Tc, 30>
{
Tc c;
Ta a;
Tb b;
foo(Ta _a, Tb _b, Tc _c) : c{_c}, a{_a}, b{_b} {}
};
template <typename Ta, typename Tb, typename Tc>
struct foo<Ta, Tb, Tc, 31>
{
Tc c;
Tb b;
Ta a;
foo(Ta _a, Tb _b, Tc _c) : c{_c}, b{_b}, a{_a} {}
};
template <typename Ta, typename Tb, typename Tc>
struct bar: public foo<Ta, Tb, Tc>
{
private:
using base = foo<Ta, Tb, Tc>;
public:
bar() = default;
using base::base;
};
template <typename Ta, typename Tb, typename Tc>
struct foobar
{
Ta a;
Tb b;
Tc c;
foobar() = default;
foobar(Ta _a, Tb _b, Tc _c) : a{_a}, b{_b}, c{_c} {}
};
} //namespace so
int main()
{
so::bar<std::uint16_t, std::uint32_t, std::uint16_t> bar{1, 2, 3};
so::foobar<std::uint16_t, std::uint32_t, std::uint16_t> foobar{1, 2, 3};
std::cout << sizeof(bar) << "\t" << sizeof(foobar) << std::endl;
std::cout << bar.a << " : " << bar.b << " : " << bar.c << std::endl;
std::cout << foobar.a << " : " << foobar.b << " : " << foobar.c << std::endl;
return (0);
}
программы:
8 12
1 : 2 : 3
1 : 2 : 3
Вопросы:
- I Есть ли какой-то известный, независимый от компилятора способ решения такой вещи (возможно, Boost)?
- Если нет, существуют ли какие-либо специфические для компилятора директивы, которые будут делать такую вещь автоматически (без смещения данных, как в случае с GCC
__atribute__((packed))
)? - Можно ли это сделать более общим способом (возможно, с использованием вариативных шаблонов)?
Заранее благодарен!
Подсказка: я считаю, что вы можете решить проблему повторения, используя наследование, которое открывает дверь для вариативных шаблонов, однако это будет в значительной степени полагаться на компилятор, и код может быть не таким изящным (ака, вам придется использовать функция доступа к элементу, например 'get <0> (foo)'). Я также хотел бы отметить, что вы смотрите только на размеры, а не на выравнивания, на некоторых архитектурах «double» имеет длину 8 байтов, но выравнивается на границах 4 байта, поэтому ваши вычисления могут оказаться неоптимальными. –
@ MatthieuM. Вы имеете в виду какое-то рекурсивное наследование, когда мы разворачиваем пакет параметров и используем какой-то 'get <>' для навигации по нему? Выравнивание можно рассматривать как 'alignof()/std :: alignment_of <>', я думаю, спасибо за указание на это. – lapk
На самом деле, моя главная проблема заключалась в том, что, к сожалению, вы не можете использовать расширение пакета в атрибутах (раздражает), и есть различные способы справиться с этим ... и затем я понял, что я уже рассмотрел эти способы при проверке того, как 'std :: tuple' была реализована и что вместо того, чтобы пытаться повторно реализовать их с помощью ухищрений наследования, я мог бы просто повторно использовать 'std :: tuple': D –