我有這樣的代碼:
template<typename ... Args>
constexpr size_t get_init_size(Args ... args) {
return sizeof...(Args);
}
template<typename ... Args>
constexpr auto make_generic_header(Args ... args) {
constexpr size_t header_lenght = get_init_size(args...);
return header_lenght;
}
constexpr auto create_ipv4_header() {
constexpr auto x = make_generic_header(0b01, 0b10, 0b01);
return x;
}
我知道這是虛擬代碼,但我將其隔離以查找錯誤。
編譯器給我錯誤(GCC):
In instantiation of 'constexpr auto make_generic_header(Args&& ...) [with Args = {int, int, int}]':
/tmp/tmp.CaO5YHcqd8/network.h:39:43: required from here
/tmp/tmp.CaO5YHcqd8/network.h:31:22: error: 'args#0' is not a constant expression
31 | constexpr size_t header_lenght = get_init_size(args...);
| ^~~~~~~~~~~~~
我嘗試將限定符const添加到函式引數,但它同樣不起作用。理論上所有這些函式都可以在編譯時計算。但是我的知識找不到問題在哪里。
uj5u.com熱心網友回復:
constexpr 對于變數和函式來說意味著不同的東西。
對于變數,這意味著變數必須是編譯時的。因此,需要使用常量運算式對其進行初始化。
對于函式,constexpr意味著除了在運行時內部使用相同的代碼之外,該函式還可以在編譯時運行。因此,您在內部所做的一切也必須適用于運行時呼叫。
考慮到這一點,讓我們學習make_generic_header:
template<typename ... Args>
constexpr auto make_generic_header(Args ... args) {
constexpr size_t header_lenght = get_init_size(args...);
return header_lenght;
}
這里,header_lenght是一個constexpr變數,所以必須是編譯時。因此,也get_init_size(args...)必須在編譯時完成。但是,由于各種原因,引數不是常量運算式,因此這是行不通的。如果沒有作業,那就意味著make_generic_header,一個constexpr功能,是不可用的runtime1,它不適合的是在編譯時和運行時可用的需求。
修復方法很簡單:使用在兩種情況下都適用的代碼:
template<typename ... Args>
constexpr auto make_generic_header(Args ... args) {
size_t header_lenght = get_init_size(args...);
return header_lenght;
}
你發現了嗎?唯一的變化是constexpr從header_lenght. 如果這個函式在編譯時運行,它仍然會運行,整個函式呼叫運算式將是一個常量運算式。
1“但它在consteval兩者中都不起作用!” - 確實,答案中給出的推理足以滿足constexpr,但我忽略了更基本的原因,因為它與此處無關。
uj5u.com熱心網友回復:
這不是關于參考問題。引自答案https://stackoverflow.com/a/31720324:

但是,從 i 的角度來看,該參考沒有前面的初始化:它是一個引數。一旦呼叫了 ByReference,它就會被初始化。
這很好,因為 f 確實有前面的初始化。f 的初始值設定項也是一個常量運算式,因為在這種情況下隱式宣告的默認建構式是 constexpr(第 12.1/5 節)。因此 i 由一個常量運算式初始化,呼叫本身就是一個常量運算式。
而關于“前初始化”,轉引自本:
它確實意味著“被初始化”,但更重要的是關于在被評估的運算式的背景關系中先前初始化的可見性。在您的示例中,在評估 func(0) 的背景關系中,編譯器具有使用 0 來查看 rf 初始化的背景關系。但是,在僅評估 func 中的運算式 rf 的背景關系中,它沒有看到 rf 的初始化. 分析是本地的,因為它不會分析每個呼叫站點。這導致 func 中的運算式 rf 本身不是常量運算式,而 func(0) 是常量運算式。
根據您的情況,該行:
constexpr size_t header_length = get_init_size(args...);
從 來看header_length,get_init_size(args...)不是核心常量運算式,因為它的 invoke 引數來自函式的引數, args 也不是核心常量運算式。
簡單的修改就可以讓你的代碼作業,你可以去掉constexpr限定符 from header_length,或者直接回傳get_init_size(args...);inmake_generic_header的函式體。
我希望這和這也可以幫助你。
uj5u.com熱心網友回復:
我重寫了可以作業的代碼,我認為將在編譯時執行:
template<typename ... Args>
constexpr auto make_generic_header(const Args ... args) {
std::integral_constant<size_t, sizeof...(Args)> header_lenght;
return header_lenght.value;
}
constexpr auto create_ipv4_header() {
constexpr auto x = make_generic_header(0b01, 0b10, 0b01);
return x;
}
我洗掉了函式get_init_size并使用了模板引數的代碼部分(保證它將在編譯時執行)并回傳傳遞給函式的引數數量(對于所有物件值相同的 std::integral_constant 并且在編譯時知道)
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/361051.html
標籤:C 模板 C 17 常量表达式 constexpr 函数
下一篇:亂數生成模板函式(靜態斷言失敗)
