这应该是一个constexpr吗?
Should this be a constexpr or not?
考虑这个代码片段 (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 link)。奇怪的东西如下:
- 如果
constexpr
在 main
中被注释掉,那么 MSVC 会生成不正确的代码(即字符串的运行时转换),但 GCC 和 clang 都会生成正确的代码(即嵌入转换后的字符串常量进入程序集)
- 如果
constexpr
没有在 main
中注释掉,那么 MSVC 会生成正确的代码(即转换后的字符串常量嵌入到程序集中),但是 GCC 和 clang 都无法编译代码,声明 t
未由常量表达式初始化(请参阅 godbolt)。最奇怪的是 GCC 错误消息,它在错误中输出 transformed 字符串并声明它不是常量表达式。
嗯,根据 C++ 标准,哪个编译器是正确的?我应该向谁报告错误?给 GCC 和 Clang 的人还是给 Microsoft?
当 t
也声明为静态时,constexpr
声明适用于所有编译器。
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 理所当然地抱怨。
自我引用使这变得有趣和棘手。
考虑这个代码片段 (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 link)。奇怪的东西如下:
- 如果
constexpr
在main
中被注释掉,那么 MSVC 会生成不正确的代码(即字符串的运行时转换),但 GCC 和 clang 都会生成正确的代码(即嵌入转换后的字符串常量进入程序集) - 如果
constexpr
没有在main
中注释掉,那么 MSVC 会生成正确的代码(即转换后的字符串常量嵌入到程序集中),但是 GCC 和 clang 都无法编译代码,声明t
未由常量表达式初始化(请参阅 godbolt)。最奇怪的是 GCC 错误消息,它在错误中输出 transformed 字符串并声明它不是常量表达式。
嗯,根据 C++ 标准,哪个编译器是正确的?我应该向谁报告错误?给 GCC 和 Clang 的人还是给 Microsoft?
当 t
也声明为静态时,constexpr
声明适用于所有编译器。
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 理所当然地抱怨。
自我引用使这变得有趣和棘手。