c++之这应该是 constexpr 还是不

telwanggs 阅读:23 2025-02-15 21:57:57 评论:0

考虑这个代码片段( godbolt ):

#include <cstdio> 
#include <string> 
#include <string_view> 
 
struct Option 
{ 
    std::string_view name; 
 
    constexpr Option( std::string_view const n ) noexcept : name{n} {} 
}; 
 
template< std::size_t N > 
class TransformedOption : public Option 
{ 
public: 
    constexpr TransformedOption( std::string_view const nameStr ) : 
        Option{ { nameStorage_, N - 1 } } 
    { 
        for ( auto i = 0U; i < N; ++i ) 
        { 
            if ( nameStr[ i ] == '_' ) { nameStorage_[ i ] = '-'; } 
            else                       { nameStorage_[ i ] = nameStr[ i ]; } 
        } 
    } 
    private: 
        char nameStorage_[ N ] = {}; 
}; 
 
template< std::size_t N > 
constexpr TransformedOption< N > make( char const (&nameStr)[ N ] ) noexcept 
{ 
    return TransformedOption< N  >{ nameStr }; 
} 
 
int main() 
{ 
    /*constexpr*/ auto t = make( "abcd_efgh_ijkl_mnop_peqst" ); 
    std::printf( "%s\n", t.name.data() ); 
    return 0; 
} 
基本上,我想通过替换每个 _ 来执行编译时字符串转换。与 -并确保最终的二进制文件只包含转换后的字符串(而不是原始字符串)。
我试过 Clang 10.0.1、GCC 10.2 和 MSVC 19.24(见上面的 Godbolt 链接)。奇怪的东西如下:
  • 如果 constexprmain 中被注释掉,然后 MSVC 生成不正确的代码(即字符串的运行时转换),但 GCC 和 clang 生成正确的代码(即转换后的字符串常量被嵌入到程序集中)
  • 如果 constexpr未在 main 中注释掉,然后 MSVC 生成正确的代码(即转换后的字符串常量被嵌入到程序集中),但是 GCC 和 clang 都无法编译代码,说明 t未由常量表达式初始化(​​请参阅 godbolt )。最奇怪的是 GCC 错误信息,它输出 变形 string 在它的错误中,并指出它不是一个常量表达式。

  • 那么,根据 C++ 标准,哪个编译器是正确的?我应该向谁报告错误?给 GCC 和 Clang 的人还是给微软?

    请您参考如下方法:

    constexprt 时,声明适用于所有编译器也被声明为静态。

    constexpr static auto t = make( "abcd_efgh_ijkl_mnop_peqst" ); 
    
    原因是 string_view .它是一种引用类型,用于引用正在初始化的对象。因此,以一种或另一种方式,您正在初始化 contexpr指针。现在,一个 constexpr指针(未初始化为空指针)只能使用具有静态存储持续时间的对象的地址进行初始化。

    [expr.const] (emphasis mine)

    11 A constant expression is either a glvalue core constant expression that refers to an entity that is a permitted result of a constant expression (as defined below), or a prvalue core constant expression whose value satisfies the following constraints:

    • if the value is an object of class type, each non-static data member of reference type refers to an entity that is a permitted result of a constant expression,
    • if the value is of pointer type, it contains the address of an object with static storage duration, the address past the end of such an object ([expr.add]), the address of a non-immediate function, or a null pointer value,
    • if the value is of pointer-to-member-function type, it does not designate an immediate function, and
    • if the value is an object of class or array type, each subobject satisfies these constraints for the value.

    An entity is a permitted result of a constant expression if it is an object with static storage duration that either is not a temporary object or is a temporary object whose value satisfies the above constraints, or if it is a non-immediate function.


    当您声明对象为自动存储期时, string_view 中的指针不是用静态对象的地址初始化的。因此 GCC 和 Clang 提示是理所当然的。
    自我引用使这变得有趣和棘手。


    标签:C++
    声明

    1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

    关注我们

    一个IT知识分享的公众号