我試圖找到將編譯時值串列傳遞給實用程式函式的最佳方法,作為基于實際用例的練習。該串列將進行一系列操作,結果值將用于運行時的另一個操作。以下是我找到的一些解決方案,簡化為 MWE。
當然,實際用例中的操作要復雜得多,因此需要這些實用函式。
方案一:引數包
template <int number>
constexpr int sum() {
return number;
}
template <int number, int next, int... rest>
constexpr int sum() {
return number sum<next, rest...>();
}
//API:
template <int... numbers>
inline void add(int& target) {
target = sum<numbers...>();
}
...
int number = 0;
add<1, 2, 3, 4, 5>(number);
優點:
- 干凈的API
- 只需要 c 14
缺點:
- 帶有遞回的笨重實作,在操作復雜時設計和閱讀都很痛苦
解決方案2: std::array
template <size_t N, std::array<int, N> numbers>
constexpr int sum() {
int ret = 0;
for (int number : numbers)
ret = number;
return ret;
}
//API:
template <size_t N, std::array<int, N> numbers>
inline void add(int& target) {
target = sum<N, numbers>();
}
...
int number = 0;
add<5, std::array{1, 2, 3, 4, 5}>(number);
優點:
- 干凈易讀的實作,無論操作的復雜性都易于設計
缺點:
- 超級笨重的API,串列的大小必須單獨指定
- Needs c 20 to be able to pass inline
std::arrayas non-type template parameter
Solution 3: std::array wrapper
template <size_t N>
struct IntArray {
constexpr IntArray(std::array<int, N> arr_) : arr(arr_) {}
const std::array<int, N> arr;
};
template <IntArray numbers>
constexpr int sum() {
int ret = 0;
for (int number : numbers.arr)
ret = number;
return ret;
}
//API:
template <IntArray numbers>
inline void add(int& target) {
target = sum<numbers>();
}
...
int target = 0;
add<IntArray<5>({1, 2, 3, 4, 5})>(target);
Pros:
- Clean and readable implementation, easy to design no matter the complexity of operations
Cons:
- (Arguably) less but still clunky API, size of list must be specified separately
- Needs c 20 to be able to pass inline
IntArrayas non-type template parameter, and also to be able to omit theIntArraytemplate parameter value in at least the function definitions
Solution 4: std::initializer_list
template <std::initializer_list<int> numbers>
constexpr int sum() {
int ret = 0;
for (int number : numbers)
ret = number;
return ret;
}
template <std::initializer_list<int> numbers>
inline void add(int& target) {
target = sum<numbers>();
}
...
int target = 0;
add<{1, 2, 3, 4, 5}>(target);
Pros:
- Clean and readable implementation, easy to design no matter the complexity of operations
- Clean, usable and readable API
Cons:
- Doesn't actually compile (g 10.3.0 with
gnu 2a):‘std::initializer_list<int>’ is not a valid type for a template non-type parameter because it is not structural
I have no idea what "not structural" means to be very honest. I'm actually surprised and disappointed by the fact that this approach doesn't work, given that std::initializer_list is apparently fully constexpr and std::array works in the same situation. There seems to be a bug in the standard about the literalness of std::initializer_list though: https://stackoverflow.com/a/28115954/1525238 In any case, I do see this as a missed opportunity for some really cool compile-time wizardry.
The question:
Can you suggest any way to improve the solutions above in any way possible, or suggest other solutions? Ideally the "best" solution would combine all of API and implementation cleanliness and readability, while requiring as low c standard as possible.
uj5u.com熱心網友回復:
感謝 HolyBlackCat 建議std::array從包中初始化 a ,我不知道這是可能的:
template <int... numbers>
constexpr int sum() {
std::array<int, sizeof...(numbers)> arr = {numbers...};
int ret = 0;
for(int number : arr)
ret = number;
return ret;
}
//API:
template <int... numbers>
inline void add(int& target) {
target = sum<numbers...>();
}
...
int number = 0;
add<1, 2, 3, 4, 5>(number);
這使得實作更加清晰。需要 C 17。
uj5u.com熱心網友回復:
為什么不只采用一個 initializer_list 引數?
constexpr int sum(std::initializer_list<int> numbers) {
int ret = 0;
for (int number : numbers)
ret = number;
return ret;
}
int target = sum({1, 2, 3, 4, 5});
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/360352.html
標籤:c c 20 constexpr compile-time
上一篇:如何找到最接近整數的回文?
