c++ - 这应该是 constexpr 还是不是?
问题描述
考虑这个代码片段(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 链接)。奇怪的东西如下:
- 如果在
constexpr
中被注释掉main
,则 MSVC 生成不正确的代码(即字符串的运行时转换),但 GCC 和 clang 都生成正确的代码(即转换后的字符串常量嵌入到程序集中) - 如果
constexpr
未在 中注释掉main
,则 MSVC 生成正确的代码(即转换后的字符串常量嵌入到程序集中),但 GCC 和 clang 都无法编译代码,说明该代码t
不是由常量表达式初始化的(参见Godbolt)。最奇怪的是 GCC 错误消息,它在错误中输出转换后的字符串并声明它不是常量表达式。
那么,根据 C++ 标准,哪个编译器是正确的?我应该向谁报告错误?对 GCC 和 Clang 的人还是对微软?
解决方案
当也声明为静态时,该constexpr
声明适用于所有编译器。t
constexpr static auto t = make( "abcd_efgh_ijkl_mnop_peqst" );
原因是string_view
。它是一种引用类型,指向正在初始化的对象。因此,以一种或另一种方式,您正在初始化一个contexpr
指针。现在,constexpr
指针(未初始化为空指针)只能使用具有静态存储持续时间的对象的地址进行初始化。
[expr.const](强调我的)
11常量表达式要么是泛指值核心常量表达式,它指的是作为常量表达式(如下定义)的允许结果的实体,要么是其值满足以下约束的纯右值核心常量表达式:
- 如果该值是类类型的对象,则每个引用类型的非静态数据成员都引用一个实体,该实体是常量表达式的允许结果,
- 如果该值是指针类型,则它包含具有静态存储持续时间的对象的地址、超过此类对象末尾的地址 ([expr.add])、非立即函数的地址或空指针值,
- 如果该值是指向成员函数类型的指针,则它不指定立即函数,并且
- 如果该值是类或数组类型的对象,则每个子对象都满足该值的这些约束。
如果实体是具有静态存储持续时间的对象,该对象不是临时对象或者是其值满足上述约束的临时对象,或者它是非立即函数,则该实体是常量表达式的允许结果。
当您将对象声明为具有自动存储持续时间时,其中的指针string_view
不会使用静态对象的地址进行初始化。因此 GCC 和 Clang 理所当然地抱怨。
自我引用使这变得有趣和棘手。
推荐阅读
- javascript - 有人可以帮我理解比较功能是如何在这里进行排序的吗?
- typescript - Firestore 安全规则不适用于特定领域
- dns - 使用 https 将域和子域从 Route53 指向 DigitialOcean
- reactjs - 第三方子组件不会在 ReactJS 中从父级的道具更改时更改
- angularjs - 使用带有 $http 的角度工厂
- java - 将 HEIF 图像转换为 Java 中更常见的图像格式
- javascript - onchange 和 onclick 不能一致地工作
- ios - SpeechSynthesis 无法在 iOS 设备上设置语音
- excel - Excel或pentaho公式获取列中不同值的总和
- php - 联系表格 7 验证最小长度