C++ constexpr if和static_断言

C++ constexpr if和static_断言,c++,templates,constexpr,c++17,static-assert,C++,Templates,Constexpr,C++17,Static Assert,已经,在C++17的轨道上。它似乎很有用(并且可以取代SFINAE的使用),但是关于static\u 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-f

已经,在C++17的轨道上。它似乎很有用(并且可以取代SFINAE的使用),但是关于
static\u 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
}
编辑:我用例子和更详细的解释来保存这个自我回答,解释导致这个问题的误解。T.C.的简短回答就足够了

在重新阅读了该提案并在static_assert上发表声明后,我得出结论,我的担忧被误导了。首先,这里的重点应该是模板定义

畸形的;模板定义不需要诊断

如果模板被实例化,任何
静态断言都会按预期触发。这大概与我引用的说法很吻合:

。。。丢弃的语句不会实例化

这对我来说有点模糊,但我认为这意味着在丢弃的语句中出现的模板将不会被实例化。其他代码 但是必须在语法上有效。因此,当实例化包含
静态断言的模板时,丢弃的
if constexpr
子句中的
静态断言(F)
,[其中F为false,字面上或constexpr值]。或者(不需要,由编译器决定)已经在声明中,如果已知它总是错误的

例如:()

#包括
模板
constexpr void some_library_foo(){
静态断言(std::is\u same::value);
}
模板
constexpr void other_library_bar(){
静态断言(std::is\u same::value);
}
模板
constexpr void buzz(){
//此模板格式错误,(无效)无需诊断,
//因为没有可以使它有效的T
//在T.C.的回答中)。
//这也意味着这两种武器都不需要开火,但是
//在
//至少在buzz被实例化时。
静态断言(!std::is_same::value);
static_assert(false);//是否已在声明中激发
//最新版本的叮当声
}
模板
void g(){
if constexpr(IntCase){
一些图书馆;
//即使在if constexpr内,两个静态断言都将触发:
static_assert(!IntCase);//如果需要,则需要格式错误的诊断
//这是真的
static_assert(IntCase);//如果需要,则需要格式错误的诊断
//这个案子是假的
//但是,不要这样做:
static_assert(false);//格式错误,无需诊断,
//原因与buzz()相同。
}否则{
其他_库_栏();
}      
}
int main(){
g();
g();
//g();//格式错误,需要诊断
//g();//格式错误,需要诊断
}
static\u assert
上的标准文本非常短。在standardese中,这是一种使用diagnostic使程序格式错误的方法(正如@immibis也指出的):

7.6。。。如果如此转换时表达式的值为true,则声明无效。否则,程序格式不正确,并且 产生的诊断信息(1.4)应包括 字符串文字,如果提供了一个


这是关于模板的一个完善的规则-这一规则允许编译器诊断
template void f(){return 1;}
。新的更改以粗体显示:

如果出现以下情况,程序格式不正确,无需诊断:

  • 无法为模板或子语句生成有效的专门化 语句中的
    constepr if
    语句([stmt.if]) 模板
    且模板未实例化,或
  • [……]
无法为包含
static\u assert
的模板生成有效的专门化,该模板的条件是非依赖的,其计算结果为
false
,因此程序是格式错误的NDR


static\u assert
s与至少一种类型的计算结果为
true
的相关条件不受影响。

您自己的答案,可能还有T.C.的答案都不太正确

首先,“即使在
范围内,如果constexpr
,两个静态断言都将触发”这句话是不正确的。因为
if constexpr
条件取决于模板参数,所以它们不会更改。
您可以看到,如果注释掉示例代码中的
static\u assert(false)
语句和
buzz()
的定义:
static\u assert(!IntCase)
将不会触发,它将编译

此外,像
这样的东西总是会有值
!std::在丢弃的
constexpr中是否相同(且没有任何效果),如果
,即使没有
T
它们的计算结果为真
我认为“无法生成有效的专门化”是标准中的糟糕措辞(除非CPPPreference是错误的;那么T.C.将是正确的)。它应该说“可以产生”,并进一步澄清“可以”的含义

这与以下问题有关:
AlwaysFalse::value
!std::在这个上下文中是相同的吗(这就是对这个答案的评论)。
我认为它们是,因为它是“can”而不是“can”,并且在实例化时对所有类型都是错误的。
std::is_same
和非标准包装器之间的关键区别在于后者在理论上可以是专门化的(感谢cigien指出这一点并提供链接)

格式错误的NDR与否的问题也取决于模板是否实例化,这一点非常清楚。

C++20使
成为静态的_
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>();
}
#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-formed, (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-formed diagnostic required if 
                              // IntCase is true
    static_assert(IntCase) ; // ill-formed diagnostic required if 
                              // IntCase is false

    // However, don't do this:
    static_assert(false) ; // ill-formed, 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-formed, diagnostic required
    //g<float,true>(); // ill-formed, diagnostic required
}
template<typename T>
void g()
{
    if constexpr (case_1)
        // ...
    else if constexpr (case_2)
        // ...
    else
        []<bool flag = false>()
            {static_assert(flag, "no match");}();
}