![](/img/trans.png)
[英]Static array initialization in a function template in C++17 (MSVC 2019)
[英]C++17 static template lazy evaluation
考慮以下示例:
#include<iostream>
template<int n>
struct fibonacci {
static const int value = n < 0 ? 0 : fibonacci<n-1>::value + fibonacci<n-2>::value;
};
template<>
struct fibonacci<1> {
static const int value = 1;
};
template<>
struct fibonacci<0> {
static const int value = 0;
};
int main() {
std::cout << fibonacci<-1>::value << std::endl;
return 0;
}
我熟悉 C++ 中的惰性求值,並且希望當傳遞 < 0 的參數時,不會對通用斐波那契模板的 if 語句的第二個分支進行求值。 但是,編譯代碼仍會導致該分支出現無限循環:
Fibonacci.cpp: In instantiation of ‘const int fibonacci<-900>::value’:
Fibonacci.cpp:5:58: recursively required from ‘const int fibonacci<-2>::value’
Fibonacci.cpp:5:58: required from ‘const int fibonacci<-1>::value’
Fibonacci.cpp:20:33: required from here
Fibonacci.cpp:5:58: fatal error: template instantiation depth exceeds maximum of 900 (use ‘-ftemplate-depth=’ to increase the maximum)
5 | static const int value = n < 0 ? 0 : fibonacci<n-1>::value + fibonacci<n-2>::value;
| ^~~~~
為什么會這樣? 它是否特定於與模板結構相關的內容?
我熟悉 C++ 中的惰性求值,並且希望當傳遞 < 0 的參數時,不會對通用斐波那契模板的 if 語句的第二個分支進行求值。
它不需要評估。 但我們在這里不處理評估。 我們正在處理模板實例化。 您使用fibonacci<n-1>::value
,這需要實例化完整的 object 類型fibonacci<n-1>
。 必須檢查類型,以查看它是否具有可用於此類表達式的成員value
。
實例化 class 模板會導致其成員的聲明被實例化。 static 數據成員的聲明包含一個初始化程序,因此也必須對其進行實例化。 所以我們遇到了遞歸實例化模板的需要。
簡單地命名fibonacci<n-1>
不會導致它被實例化(想想前向聲明)。 如果您想延遲實例化,您必須以需要它們定義的方式延遲使用這些類型(例如訪問成員)。
舊的元編程技巧(非常符合函數式編程)涉及幫助模板。
template<class L, class R>
struct add {
static constexpr auto value = L::value + R::value;
};
template<int n>
struct fibonacci {
static const int value = std::conditional_t<(n < 0), fibonacci<0>, add<fibonacci<n-1>, fibonacci<n-2>>>::value;
};
std::conditional_t
將根據條件選擇類型。 然后,訪問該類型(並且僅該類型)的::value
。 因此,在實際需要之前,沒有任何東西是完全實例化的。
您可以使用if constexpr
:
template<int n>
struct fibonacci {
static const int value = []() {
if constexpr (n < 0) {
return 0;
} else {
return fibonacci<n-1>::value + fibonacci<n-2>::value;
}
}();
};
當fibonacci
使用n
的某個值進行實例化時,該實例化中使用的所有表達式也必須被編譯。 這意味着任何使用的模板也必須被實例化。 即使從未計算過包含模板實例化的表達式,這也是必要的。
避免在表達式中實例化模板的唯一方法是根本不編譯表達式。 這允許您避免使用不正確的 arguments 實例化模板。
您可以使用 C++17 中的if constexpr
來執行此操作:
template<int n>
struct fibonacci {
static const int value = [] {
if constexpr (n < 0) return 0;
else return fibonacci<n-1>::value + fibonacci<n-2>::value;
}();
};
這是一個演示。
Artefacto和Story Teller-Unslander Monica已經提供了正確的答案。
我只是想在cppinsights的幫助下進一步解釋
出於解釋目的,請考慮稍加修改的原始代碼:
...
template<int n>
struct fibonacci {
static const int value = n > 0 ? 0 : fibonacci<n-1>::value + fibonacci<n-2>::value;
};
...
int main() {
std::cout << fibonacci<3>::value << std::endl; // modified parameter from -1 to 3 to avoid compilation failure by recursive instantiation
return 0;
}
這是編譯器生成的樣子
...
#ifdef INSIGHTS_USE_TEMPLATE
template<>
struct fibonacci<3>
{
static const int value = 3 > 0 ? 0 : fibonacci<2>::value + fibonacci<1>::value;
};
#endif
/* First instantiated from: insights.cpp:5 */
#ifdef INSIGHTS_USE_TEMPLATE
template<>
struct fibonacci<2>
{
static const int value = 2 > 0 ? 0 : fibonacci<1>::value + fibonacci<0>::value;
};
#endif
...
int main()
{
std::cout.operator<<(fibonacci<2>::value).operator<<(std::endl);
return 0;
}
從上面生成的代碼可以清楚地看出,即使表達式3 > 0
為真,編譯器仍會在n = 2
和n = 1
的false
表達式中實例化模板
現在看一下Artefacto共享的if constexpr
的代碼。 IE
template<int n>
struct fibonacci {
static const int value = []() {
if constexpr (n < 0) {
return 0;
} else {
return fibonacci<n-1>::value + fibonacci<n-2>::value;
}
}();
};
將模板參數視為-1
。 這是編譯器將如何解釋它
#ifdef INSIGHTS_USE_TEMPLATE
template<>
struct fibonacci<-1>
{
class __lambda_5_30
{
public:
inline /*constexpr */ int operator()() const
{
if constexpr(-1 < 0) {
return 0;
}
}
using retType_5_30 = int (*)();
inline /*constexpr */ operator retType_5_30 () const noexcept
{
return __invoke;
};
private:
static inline int __invoke()
{
if constexpr(-1 < 0) {
return 0;
}
}
} __lambda_5_30{};
static const int value = __lambda_5_30.operator()();
class __anon_5_30;
};
#endif
從上面的代碼可以清楚地看出,編譯器甚至沒有考慮if constexpr
的 else 部分。 這就是為什么這段代碼可以完全正常工作的原因。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.