我一直在關注這個創建可變引數結構的教程,它與另一個關于從頭開始創建基本元組的教程幾乎相同。不幸的是,當我分析可變引數結構時,它似乎效率很低。結構的大小似乎臃腫,因為結構的大小似乎與其可變布局不匹配。似乎位元組對齊不是問題,因為實際的元組似乎沒有受到這種影響,所以我想知道它們是如何繞過它的,或者我在我的結構中做錯了什么。
下面是我用來測驗可變引數結構的代碼:
#include <iostream>
#include <tuple>
#include <array>
template<typename ... T>
struct DataStructure
{
};
template<typename T>
struct DataStructure<T> {
DataStructure(const T& first) : first(first)
{}
DataStructure() {}
T first;
};
template<typename T, typename ... Rest>
struct DataStructure<T, Rest ...>
{
DataStructure(const T& first, const Rest& ... rest)
: first(first)
, rest(rest...)
{}
DataStructure() {}
T first;
[[no_unique_address]] DataStructure<Rest ... > rest;
};
struct test1 {
int one;
float two;
};
struct test2 {
double three;
float two;
int one;
};
int main()
{
std::cout << "Size of test1 with double: " << sizeof(test1) << std::endl;
std::cout << "Offset of test1 with double: " << offsetof(test1, one) << " | " << offsetof(test1, two) << std::endl;
std::cout << std::endl;
typedef DataStructure<int32_t, float> def;
std::cout << "Size of DataStructure<int32_t, float> w/o Double: " << sizeof(def) << std::endl;
std::cout << "Offset of DataStructure<int32_t, float> w/o Double: " << offsetof(def, first) << " | " << offsetof(def, rest.first) << std::endl;
std::cout << std::endl;
std::cout << "Size of test2 with double: " << sizeof(test2) << std::endl;
std::cout << "Offset of test2 with double: " << offsetof(test2, one) << "(int32) | " << offsetof(test2, two) << "(float) | " << offsetof(test2, three) << "(double)" << std::endl;
std::cout << std::endl;
typedef DataStructure<double, float, int32_t> defDouble;
std::cout << "Size of DataStructure<double, float, int32_t>: " << sizeof(defDouble) << std::endl;
std::cout << "Offset of DataStructure<double, float, int32_t>: " << offsetof(defDouble, rest.rest.first) << "(int32) | " << offsetof(defDouble, rest.first) << "(float) | " << offsetof(defDouble, first) << "(double)" << std::endl;
std::cout << std::endl;
std::tuple<int32_t, float, double> tp;
std::cout << "Size of tuple with double (gcc compiled tuple reverses parameter layout in memory): " << sizeof(tp) << std::endl;
std::cout << "Offset of tuple with double: " << (long)&std::get<0>(tp) - (long)&tp << "(int32) | " << (long)&std::get<1>(tp) - (long)&tp << "(float) | " << (long)&std::get<2>(tp) - (long)&tp << "(double)" << std::endl;
std::cout << std::endl;
std::cout << "Size of no parameter DataStructure<>: " << sizeof(DataStructure<>) << std::endl;
typedef DataStructure<int32_t, float, double> defDoubleNormal;
std::cout << "Size of DataStructure<int32_t, float, double>: " << sizeof(defDoubleNormal) << std::endl;
std::cout << "Offset of DataStructure<int32_t, float, double>: " << offsetof(defDoubleNormal, first) << "(int32) | " << offsetof(defDoubleNormal, rest.first) << "(float) | " << offsetof(defDoubleNormal, rest.rest.first) << "(double)" << std::endl;
std::cout << std::endl;
std::tuple<double, float, int32_t> tp2;
std::cout << "Size of tuple with double (gcc compiled tuple reverses parameter layout in memory): " << sizeof(tp) << std::endl;
std::cout << "Offset of tuple with double: " << (long)&std::get<0>(tp2) - (long)&tp2 << "(double) | " << (long)&std::get<1>(tp2) - (long)&tp2 << "(float) | " << (long)&std::get<2>(tp2) - (long)&tp2 << "(int32)" << std::endl;
std::cout << std::endl;
std::array<defDouble, 2> arr;
std::cout << "Array DataStructure<double, float, int32_t> Offsets: [0] - " << (long)&arr[0] - (long)&arr << ", [1] - " << (long)&arr[1] - (long)&arr;
}
上面的代碼列印:
Size of test1 with double: 8
Offset of test1 with double: 0 | 4
Size of DataStructure<int32_t, float> w/o Double: 8
Offset of DataStructure<int32_t, float> w/o Double: 0 | 4
Size of test2 with double: 16
Offset of test2 with double: 12(int32) | 8(float) | 0(double)
Size of DataStructure<double, float, int32_t>: 16
Offset of DataStructure<double, float, int32_t>: 12(int32) | 8(float) | 0(double)
Size of tuple with double (gcc compiled tuple reverses parameter layout in memory): 16
Offset of tuple with double: 12(int32) | 8(float) | 0(double)
Size of no parameter DataStructure<>: 1
Size of DataStructure<int32_t, float, double>: 24
Offset of DataStructure<int32_t, float, double>: 0(int32) | 8(float) | 16(double)
Size of tuple with double (gcc compiled tuple reverses parameter layout in memory): 16
Offset of tuple with double: 8(double) | 4(float) | 0(int32)
Array DataStructure<double, float, int32_t> Offsets: [0] - 0, [1] - 16
我顛倒了帶有 double 的 DataStructure 和元組之間模板引數的順序,因為我在內部使用的元組顛倒了成員的順序(這是元組的一個未指定的實作細節,也是 gcc 是如何實作它的);這樣,兩個成員布局的順序相同,雙精度從偏移量 0 開始,然后是它們的浮點數,最后是它們的 int32。在 test2 結構體中可以直觀地看到該布局我們可以看到,“test2”所示的結構體的適當大小是 16,成員的偏移量是雙精度為 0,浮點數為 8 和 12對于 int32。元組顯示了相同的內容,盡管成員順序顛倒了,double 為 0,float 為 8,int32 為 12,總大小為 16 位元組。在 DataStructure 中可以找到這些相同的偏移量;double 在偏移量 0 處,浮點數在 8 處,int32 處在 12 處,但這次總大小為 24 位元組,我無法理解在結構末尾填充的內容或原因。我知道雙打需要 8 位元組對齊,但這在這里應該不是問題,而且顯然不像元組和 test2 的情況所示。
uj5u.com熱心網友回復:
即使是空類也需要空間來存盤自己,因此類的最小大小為1。由于您的無引數DataStructure類是空的,并且有一個成員會占用空間并導致其余成員占用更多空間以允許對齊。使基礎非空解決了這個問題:
template<typename ... T>
struct DataStructure;
template<typename T>
struct DataStructure<T>
{
DataStructure(const T& first)
: first(first)
{}
T first;
};
template<typename T, typename ... Rest>
struct DataStructure<T, Rest ...>
{
DataStructure(const T& first, const Rest& ... rest)
: first(first)
, rest(rest...)
{}
T first;
DataStructure<Rest ... > rest;
};
在 的情況下,這仍然會引入額外的填充DataStructure<int32_t, float, double>。這是因為您的代碼本質上會產生:
struct A
{
double a;
};
struct B
{
A a;
float b;
};
struct C
{
B b;
int32_t c;
};
編譯器將嘗試始終將 放在double8 位元組的倍數上,因為B需要 12 位元組,編譯器將其填充為 16 位元組到 , 陣列中的B,a始終與 8 位元組對齊。sizeof(B)因此將是 16 導致sizeof(C)24。
我認為std::tuple通常使用基類而不是成員來實作。更改您的實作以執行此操作也可以解決此填充問題:
template<typename ... T>
struct DataStructure;
template<typename T>
struct DataStructure<T>
{
DataStructure(const T& first)
: first(first)
{}
T first;
};
template<typename T, typename ... Rest>
struct DataStructure<T, Rest ...>: DataStructure<Rest ... >
{
DataStructure(const T& first, const Rest& ... rest)
: first(first), DataStructure<Rest ... >(rest...)
{}
T first;
};
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/380297.html
下一篇:可變引數模板呼叫assert()
