Joh*_*erg 31 c++ templates static-assert constexpr c++17
P0292R1 constexpr如果已包含在内,正在进行 C++ 17.它似乎很有用(并且可以取代SFINAE的使用),但关于static_assert形成不良的评论,假分支中不需要诊断会让我害怕:
Disarming static_assert declarations in the non-taken branch of a
constexpr if is not proposed.
void f() {
if constexpr (false)
static_assert(false); // ill-formed
}
template<class T>
void g() {
if constexpr (false)
static_assert(false); // ill-formed; no
// diagnostic required for template definition
}
Run Code Online (Sandbox Code Playgroud)
我认为它是完全禁止static_assert在constexpr内部使用if(至少是假/非分支,但实际上这意味着它不是一个安全或有用的事情).
这是如何从标准文本中产生的?我发现static_assert在提案中没有提到措辞,并且C++ 14 constexpr函数允许static_assert(cppreference的详细信息:constexpr).
是否隐藏在这个新句子中(6.4.1之后)?:
当constexpr if语句出现在模板化实体中时,在封闭模板或通用lambda的实例化期间,不会实例化丢弃的语句.
从那以后,我认为它也是禁止的,不需要诊断,调用调用图中某个地方可能调用的其他constexpr(模板)函数static_assert.
底线:
如果我的理解是正确的,那么对于constexpr if我们必须知道(从文档或代码检查)有关任何使用的安全性和实用性,这是不是一个非常严格的限制static_assert?我的担忧是否错位?
更新:
这段代码在没有警告的情况下编译(clang head 3.9.0)但是我的理解是不正确的,不需要诊断.是否有效?
template< typename T>
constexpr void other_library_foo(){
static_assert(std::is_same<T,int>::value);
}
template<class T>
void g() {
if constexpr (false)
other_library_foo<T>();
}
int main(){
g<float>();
g<int>();
}
Run Code Online (Sandbox Code Playgroud)
T.C*_*.C. 24
这是关于模板的完善规则 - 允许编译器诊断的相同规则template<class> void f() { return 1; }.[temp.res]/8,新变化加粗:
如果出现以下情况,该计划格式错误,无需诊断:
- 无法为模板中的模板或子
constexpr if语句([stmt.if])生成有效的专门化,并且模板未实例化,或者- [...]
对于包含static_assert其条件不依赖且计算结果的模板,不能生成有效的特化false,因此程序是格式错误的NDR.
static_assert具有可以评估true至少一种类型的依赖条件的s 不受影响.
Nik*_* C. 15
C++20现在static_assert的else分支if constexpr变得更短了,因为它允许模板 lambda 参数。因此,为了避免格式错误的情况,我们现在可以定义一个带有bool模板非类型参数的 lambda,用于触发static_assert. 我们立即使用 调用 lambda (),但由于如果else不采用其分支,则不会实例化 lambda ,因此除非else实际采用,否则不会触发断言:
template<typename T>
void g()
{
if constexpr (case_1)
// ...
else if constexpr (case_2)
// ...
else
[]<bool flag = false>()
{static_assert(flag, "no match");}();
}
Run Code Online (Sandbox Code Playgroud)
编辑:我将通过示例以及对导致此问题的误解的更详细解释保持自我解答。TC的简短回答就足够了。
重读的建议后并static_assert在目前的草案,我得出结论,我的担心是错误的。首先,这里的重点应该放在模板定义上。
格式错误 模板定义无需诊断
如果实例化了模板,则会static_assert发生任何预期的火灾。这大概可以与我引用的语句配合使用:
...废弃的语句未实例化。
这对我来说有点模糊,但是我得出的结论是,这意味着不会实例化在废弃语句中出现的模板。但是,其他代码必须在语法上有效。因此,当实例化包含的模板时,static_assert(F)被丢弃的if constexpr子句中的A (其中F为假,无论是字面值还是constexpr值)仍将“咬” static_assert。或者(如果不是必需的,则由编译器决定)是否已经声明总是为假。
示例:(现场演示)
#include <type_traits>
template< typename T>
constexpr void some_library_foo(){
static_assert(std::is_same<T,int>::value);
}
template< typename T>
constexpr void other_library_bar(){
static_assert(std::is_same<T,float>::value);
}
template< typename T>
constexpr void buzz(){
// This template is ill-formated, (invalid) no diagnostic required,
// since there are no T which could make it valid. (As also mentioned
// in the answer by T.C.).
// That also means that neither of these are required to fire, but
// clang does (and very likely all compilers for similar cases), at
// least when buzz is instantiated.
static_assert(! std::is_same<T,T>::value);
static_assert(false); // does fire already at declaration
// with latest version of clang
}
template<class T, bool IntCase>
void g() {
if constexpr (IntCase){
some_library_foo<T>();
// Both two static asserts will fire even though within if constexpr:
static_assert(!IntCase) ; // ill-formated diagnostic required if
// IntCase is true
static_assert(IntCase) ; // ill-formated diagnostic required if
// IntCase is false
// However, don't do this:
static_assert(false) ; // ill-formated, no diagnostic required,
// for the same reasons as with buzz().
} else {
other_library_bar<T>();
}
}
int main(){
g<int,true>();
g<float,false>();
//g<int,false>(); // ill-formated, diagnostic required
//g<float,true>(); // ill-formated, diagnostic required
}
Run Code Online (Sandbox Code Playgroud)
标准文字static_assert非常简短。在standardese,它是一种方法,使程序形成不良与诊断(如@immibis还指出):
7.6 ...如果转换后的表达式的值为true,则声明无效。否则,程序格式不正确,如果提供了此信息,则诊断信息(1.4)应包含字符串文字的文本...
我遇到的解决这个问题的最简洁的方法(至少在当前的编译器中)是使用!sizeof(T*)for 条件,Raymond Chen 在这里详细介绍了这一点。这有点奇怪,从技术上讲并不能解决格式错误的问题,但至少它很短并且不需要包含或定义任何内容。一条解释它的小评论可能会对读者有所帮助:
template<class T>
void g() {
if constexpr (can_use_it_v<T>) {
// do stuff
} else {
// can't use 'false' -- expression has to depend on a template parameter
static_assert(!sizeof(T*), "T is not supported");
}
}
Run Code Online (Sandbox Code Playgroud)
使用的目的T*是仍然为不完整的类型给出正确的错误。
我还在旧的 isocpp 邮件列表中遇到了这个讨论,这可能会增加这个讨论。有人提出了一个有趣的观点,即执行这种条件static_assert并不总是最好的主意,因为它不能用于 SFINAE-away 重载,这有时是相关的。