【发布时间】:2020-11-30 05:19:20
【问题描述】:
考虑一下这段代码 sn-p (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(请参阅上面的 gobolt 链接)。奇怪的东西如下:
- 如果
constexpr在main中被注释掉,那么MSVC 会生成不正确的代码(即字符串的运行时转换),但GCC 和clang 都会生成正确的代码(即转换后的字符串常量嵌入到程序集中)李> - 如果
constexpr没有在main中被注释掉,那么MSVC 会生成正确的代码(即转换后的字符串常量嵌入到程序集中),但是GCC 和clang 都无法编译代码,说明t不是由常量表达式初始化的(参见godbolt)。最奇怪的是 GCC 错误消息,它在错误中输出 transformed 字符串并声明它不是常量表达式。
那么,根据 C++ 标准,哪个编译器是正确的?我应该向谁报告错误?对 GCC 和 Clang 人员还是对 Microsoft?
【问题讨论】:
-
"MSVC 生成不正确的代码(即字符串的运行时转换)" 那是正确的代码。除非有必要,否则编译器不需要在编译时做任何事情。
constexpron functions 是一个选项,除非在需要常量表达式的上下文中调用。 -
有趣的是,一旦你摆脱了
Option基类(并根据需要通过函数调用生成string_view),the code works just fine on all compilers。因此,基类中可能发生了一些让编译器感到困惑的事情。 -
@NicolBolas,我知道这是正确的代码。不过,这对我的事业来说是不正确的(即,这不是我想要实现的目标)。