请参考以下代码段:
#include <type_traits>
#include <string_view>
constexpr std::size_t strlen(char const* s) {
std::size_t n = 0;
while (*s++ != '\0')
++n;
return n;
}
template <std::size_t>
struct X {};
int main() {
constexpr auto pf = __PRETTY_FUNCTION__; // gcc ok; clang ok; (1)
static_assert(std::string_view(__PRETTY_FUNCTION__) == std::string_view("int main()")); // gcc ok; clang ok; (2)
X<strlen(__PRETTY_FUNCTION__)> x; // gcc not ok; clang ok; (3)
}
Clang 8可以编译它,但是GCC 8.3不能。参见godbolt。尽管行(3)
和(1)
没问题,但GCC在行(2)
上失败了。
如果我能够在pf
行中声明(1)
并在__PRETTY_FUNCTION__
中使用static_assert
,则意味着表达式__PRETTY_FUNCTION__
是核心常量表达式。而且如果我无法声明X<strlen(__PRETTY_FUNCTION__)> x
,则意味着strlen(__PRETTY_FUNCTION__)
不是整数常量表达式。
那么为什么strlen(__PRETTY_FUNCTION__)
不是整数常量表达式?是标准隐含的,还是GCC错误?
答案 0 :(得分:5)
numeric
不是标准的。这样,编译器可以在不同的地方(在解析,构建AST或链接时)实现它。
如果应该在解析时实现,则它可以是一个常量表达式(我想这就是clang所做的事情)。 但是,如果它是在链接时实现的(也就是说,编译器会为其发出符号,而链接器将对其进行解析),则它不能是常量表达式。
我认为海湾合作委员会使用后一种情况。
请注意,在这种情况下,您可以使用它们的sizeof(),因为如果需要编译时常量字符串的长度计算,它是Bitmap Bolden(Bitmap bmp0)
{
float f = 2f;
Bitmap bmp = new Bitmap(bmp0.Width, bmp0.Height);
using (Bitmap bmp1 = new Bitmap(bmp0, new Size((int)( bmp0.Width * f),
(int)( bmp0.Height * f))))
{
float contrast = 1f;
ColorMatrix colorMatrix = new ColorMatrix(new float[][]
{
new float[] {contrast, 0, 0, 0, 0},
new float[] {0,contrast, 0, 0, 0},
new float[] {0, 0, contrast, 0, 0},
new float[] {0, 0, 0, 1, 0},
new float[] {0, 0, 0, 0, 1}
});
ImageAttributes attributes = new ImageAttributes();
attributes.SetColorMatrix(colorMatrix, ColorMatrixFlag.Default,
ColorAdjustType.Bitmap);
attributes.SetGamma(7.5f, ColorAdjustType.Bitmap);
using (Graphics g = Graphics.FromImage(bmp))
g.DrawImage(bmp1, new Rectangle(0, 0, bmp.Width, bmp.Height),
0, 0, bmp1.Width, bmp1.Height, GraphicsUnit.Pixel, attributes);
}
return bmp;
}
。
因此,将表达式3替换为:
__PRETTY_FUNCTION__
,它将在两个编译器上都可以正常编译。
编辑:正如NathanOliver指出的那样,GCC似乎将const char[]
的签名视为X<sizeof(__PRETTY_FUNCTION__) - 1> x;
,而clang / visual studio则将其视为__PRETTY_FUNCTION__
。这在GCC中是一个令人讨厌的麻烦(不是错误,因为它不是标准的),并且他们似乎已在> 8.0.0版本中对其进行了修复。
在表达式(1)和表达式(2)中,static const char[]
衰减为static constexpr const char[]
(指针是常数,但是关于数据什么也不能说)。对我来说,表达式(2)不能证明任何事情,因为不能保证指针在相等的两面都应该相等,即使它们指向“相同”的内容也是如此。 __PRETTY_FUNCTION__
构造函数期望const char*
,因此,除string_view
之外的任何可能衰减到const char*
的东西都将传递表达式(2)。
答案 1 :(得分:0)
我仍然担心(2)
行,因此我发现注释中不适合的内容。
我对代码段做了一些修改,请参阅this:
#include <type_traits>
#include <string_view>
constexpr std::size_t strlen(char const* s) {
std::size_t n = 0;
while (*s++ != '\0')
++n;
return n;
}
template <std::size_t>
struct X {};
static char const PF[] = "int main()";
int main() {
constexpr auto pf = std::string_view(__PRETTY_FUNCTION__); // gcc ok; clang ok; (1)
static_assert(pf == std::string_view("int main()")); // gcc ok; clang ok; (2)
X<strlen(__PRETTY_FUNCTION__)> x; // gcc not ok; clang ok; (3)
static_assert(pf[0] == 'i'); // not ok; (4)
X<pf.size()> x1; // ok; (5)
X<strlen(pf.data())> x2; // not ok; (6)
static_assert(__builtin_constant_p(pf.data()) == 0); // ok (7)
static_assert(__builtin_strlen(pf.data()) == 10); // ok (8)
static_assert(__builtin_memcmp(pf.data(), "int main()", __builtin_strlen(pf.data())) == 0); // ok (9)
static_assert(std::char_traits<char>::compare(pf.data(), "int main()", std::char_traits<char>::length(pf.data())) == 0); // ok (10)
static_assert(std::char_traits<char>::length(PF) == 10); // not ok (11)
static_assert(__builtin_strlen(PF) == 10); // not ok (12)
}
据我了解,如果static_assert
不是(2)
表达式是constexpr(如第believe
行),(4)
将无法证明(2)
的值。但是,尽管(4)
和(5)
行有问题,但std::char_traits<char>
行对于GCC似乎还可以。因此,我偷看了__builtin_constant_p
。这些特征使用__builtin_*
在strlen
实现和我的(7)
之类的实现之间分配。第__PRETTY_FUNCTION__
行指出“无法证明__builtin_memcmp(__PRETTY_FUNCTION__)
的恒定性”(请参阅gcc doc),尽管表达式(8)
可以在编译时求值(请参阅第(11)
行)。
在(12)
和__PRETTY_FUNCTION__
中考虑失败,可以得出结论,在某些情况下,static constexpr char const []
的行为就好像它被宣布为static char const []
,而在其他情况下,则是{ {1}}。换句话说,如果__PRETTY_FUNCTION__
具有类型,则在编译的所有步骤中它都是不一致的(我参考GCC 8.3)。