首页 文章

constexpr if和static_assert

提问于
浏览
25

P0292R1 constexpr if已经included,正在进行C17 . 它似乎很有用(并且可以取代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
}

我认为完全禁止在constexpr中使用 static_assert (至少是假/非分支,但实际上这意味着它不是一个安全或有用的东西) .

这是如何从标准文本中产生的?我在提案措辞中没有提到 static_assert ,而且C 14 constexpr函数允许 static_assert (cppreference的详细信息:constexpr) .

是否隐藏在这个新句子中(6.4.1之后)? :

当constexpr if语句出现在模板化实体中时,在封闭模板或通用lambda的实例化期间,不会实例化丢弃的语句 .

从那以后,我假设它也被禁止,不需要诊断,调用调用图下面某处可能调用 static_assert 的其他constexpr(模板)函数 .

Bottom line:

如果我的理解是正确的,那么对于 constexpr if 的安全性和有用性是不是非常严格限制,因为我们必须知道(从文档或代码检查)有关 static_assert 的任何使用?我的担忧是否错位?

Update:

这段代码在没有警告的情况下编译(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>();
}

2 回答

  • 2

    这是关于模板的完善规则 - 允许编译器诊断 template<class> void f() { return 1; } 的相同规则 . [temp.res]/8以新的变化加粗:

    如果以下情况,程序格式错误,无需诊断:无法为模板生成有效的特化,或者模板中的constexpr if语句([stmt.if])并且模板未实例化,或[ ...]

    无法为包含 static_assert 的模板生成有效的专业化,该模板的条件是非依赖的并且计算结果为 false ,因此程序是格式错误的NDR .

    具有依赖条件的 static_assert s可以评估至少一种类型的 true 不受影响 .

  • 21

    Edit: 我通过实例和更详细解释导致这些问题的误解来保持这种自我回答 . T.C.的简短回答是严格的 .

    重新阅读提案并在current draftstatic_assert 上,我得出结论,我的担忧是错误的 . 首先,这里的重点应该放在模板定义上 .

    形象不对称;模板定义无需诊断

    如果实例化模板,则按预期方式激活任何 static_assert . 这可能与我引用的陈述很好地吻合:

    ...丢弃的语句未实例化 .

    这对我来说有点模糊,但我得出结论,这意味着丢弃语句中出现的模板不会被实例化 . 但是其他代码必须在语法上有效 . 因此,当实例化包含 static_assert 的模板时,丢弃的 if constexpr 子句中的 static_assert(F) ,[其中F为假,字面或constexpr值]仍然是'bite' . 或者(不是必需的,在编译器的支配下)已经在声明中,如果已知它总是假的 .

    示例:(live demo

    #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
    }
    

    static_assert 上的标准文字非常短 . 在标准中,这是一种通过诊断使程序格式错误的方法(正如@immibis也指出的那样):

    7.6 ...如果转换后的表达式的值为true,则声明无效 . 否则,程序格式错误,结果诊断消息(1.4)应包括字符串文字的文本,如果提供的话......

相关问题