考慮一個功能對象F
服用constexpr size_t
參數I
適配非constexpr積分值與非類型模板參數,和代碼膨脹
struct F
{
template <size_t I>
constexpr size_t operator()(size <I>) const { return I; }
};
纏繞的類型size <I>
內,其中(爲了簡潔)
template <size_t N>
using size = std::integral_constant <size_t, N>;
當然,我們可以直接通過I
,但我想強調它是通過使用它作爲模板參數的constexpr。函數F
在這裏是虛構的,但實際上它可以執行各種有用的東西,比如從元組的元素I
中檢索信息。假定F
具有相同的返回類型,而不管I
。 I
可以是任何整數類型,但假定爲非負值。
問題
給定一個constexpr size_t
值I
,我們可以通過
F()(size <I>());
打電話F
現在,如果我們要調用F
與非constepr size_t
值i
什麼?考慮以下幾點:?
constexpr size_t L = 10;
idx <F, L> f;
for (size_t i = 0; i < L; ++i)
cout << f(i) << " ";
(爲什麼需要這個要給予一定的背景下,我其實想建立一個複合迭代器的容器觀點,即表示「加入」序列(串聯)異構容器中,這樣可以說join(a, b) = c;
這樣的數組,其中數組join(a, b)
和c
長度相等,但是i
是迭代器狀態,因此不能是constexpr
,但子迭代器存儲在元組中,需要被constexpr
訪問指數個人value_type
的大致一致,以便加入的視圖可以採取他們的common_type
類型,但子容器和因此子迭代器是不同類型的。)
解
在這裏,我已經想出結構idx <F, L>
,其適應功能F
用於此目的,假設輸入參數小於L
。這實際上編譯罰款輸出
0 1 2 3 4 5 6 7 8 9
這裏是一個live example。
idx
作品通過遞歸分解輸入i
成二進制表示和重建constexpr對方N
:
template <typename F, size_t L, size_t N = 0, bool = (N < L)>
struct idx
{
template <size_t R = 1>
inline constexpr decltype(F()(size <0>()))
operator()(size_t I, size <R> = size <R>()) const
{
return I%2 ? idx <F, L, N+R>()(--I/2, size <2*R>()) :
I ? idx <F, L, N >()( I/2, size <2*R>()) :
F()(size <N>());
}
};
其中R
表示2
在當前迭代的功率。爲了避免無限模板實例化,專業化,給出了N >= L
,返回F()(size <0>())
爲虛值:
template <typename F, size_t L, size_t N>
struct idx <F, L, N, false>
{
template <size_t R>
inline constexpr decltype(F()(size <0>()))
operator()(size_t I, size <R>) const { return F()(size <0>()); }
};
事實上,這種方法是比較常見的成語有一個布爾參數的概括:
bool b = true;
b ? f <true>() : f <false>();
其中f
是以bool
作爲模板參數的函數。在這種情況下,顯然所有兩個可能的版本f
都被實例化了。
問題
雖然這個作品和它的運行時間複雜度爲i
想必對數,我關心的是編譯時的影響,如:
的
idx
多少組合並且它的template operator()
被實例化爲在運行時爲編譯時未知的任何輸入i
工作? (我明白「所有可能」,但又有多少?)是否真的可以內聯
operator()
?是否有任何替代策略或變體「更容易」編譯?
作爲純code bloat的實例,我應該忘記這個想法嗎?
注
下面是編譯時間(秒)和可執行的大小(單位爲KB)我已經測量了不同的值的L
:
L Clang(s) GCC(s) Clang(KB) GCC(KB)
10 1.3 1.7 33 36
20 2.1 3.0 48 65
40 3.7 5.8 87 120
80 7.0 11.2 160 222
160 13.9 23.4 306 430
320 28.1 47.8 578 850
640 56.5 103.0 1126 1753
所以,雖然它在L
出現大致線性,這是相當長和令人沮喪的大。
試圖強制operator()
內聯失敗:可能被Clang忽略(可執行的更大),而GCC報告recursive inlining
。
在可執行文件上運行nm -C
對於L = 160
,顯示511/1253
不同版本的operator()
(與Clang/GCC)。這些全部用於N < L
,所以看起來終止專用N >= L
確實被內聯。
PS。我會添加標籤code-bloat
但系統不會讓我。
您是否期望'L'永遠大於5? – ecatmur
在我的應用程序中(在我給出的上下文中),沒有理由不想連接例如105陣列,特別是如果我們最終考慮多維的...... – iavr