__PRETTY_FUNCTION__在不断的表达中

__PRETTY_FUNCTION__ in constant expression

本文关键字:PRETTY FUNCTION      更新时间:2023-10-16

请参考这个片段:

#include <type_traits>
#include <string_view>
constexpr std::size_t strlen(char const* s) {
std::size_t n = 0;
while (*s++ != '')
++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 没有编译。见神电。GCC 联机(3)失败,尽管行(1)(2)正常。 如果我能够在第(1)行中声明pf并在static_assert中使用__PRETTY_FUNCTION__,则意味着表达式__PRETTY_FUNCTION__是核心常量表达式。如果我不能声明X<strlen(__PRETTY_FUNCTION__)> x这意味着strlen(__PRETTY_FUNCTION__)不是一个完整的常量表达式。

那么为什么strlen(__PRETTY_FUNCTION__)不是积分常量表达式呢?它是标准暗示的,还是GCC错误?

__PRETTY_FUNCTION__不是标准的。因此,编译器可以在不同的地方实现它(解析时、构建 AST 时或链接时)。

如果它应该在解析时实现,那么它可以是一个常量表达式(我想这就是 clang 正在做的事情)。 但是,如果在链接时实现它(即,编译器为其发出一个符号,链接器将解析它),则它不能是常量表达式。

我认为海湾合作委员会使用后一种情况。

请注意,在这种情况下,您可以采用其中的 sizeof(),因为如果您需要编译时常量字符串的长度计算,这是一个const char[]。 因此,将表达式 3 替换为:

X<sizeof(__PRETTY_FUNCTION__) - 1> x;

它可以在两个编译器上都能很好地编译。

编辑:正如NathanOliver指出的那样,海湾合作委员会似乎认为__PRETTY_FUNCTION__的签名是static const char[],而clang/visual studio认为它是static constexpr const char[]。这在 GCC 中是一个痛苦的麻烦(不是错误,因为它不是标准的),他们似乎已经在>8.0.0 版本中修复了它。

在表达式 (1) 和表达式 (2) 中,__PRETTY_FUNCTION__衰减为const char*(指针是常数,但对数据没有任何说明)。对我来说,表达式(2)可能无法证明什么,因为不能保证指针在平等的两侧应该是相等的,即使它们指向"相同"的内容。string_view构造函数期望const char*,因此除了__PRETTY_FUNCTION__之外的任何可能衰减到const char*的东西都会传递表达式(2)。

我仍然担心行(2),所以我想出了一些不适合评论的东西。

我稍微修改了一下片段,请参考这个:

#include <type_traits>
#include <string_view>
constexpr std::size_t strlen(char const* s) {
std::size_t n = 0;
while (*s++ != '')
++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)
}

据我了解,如果不believe表达式是constexpr(如第(4)行的情况),static_assert将无法证明(2)的值。但是,尽管线路有问题(2)(4)(5)线路对海湾合作委员会来说似乎还可以。所以,我偷看了std::char_traits<char>.这些特征使用__builtin_constant_p__builtin_*实现和像我的strlen这样的实现之间调度。(7)行声明"无法证明__PRETTY_FUNCTION__的一致性"(参见 gcc 文档),但尽管如此,表达式__builtin_memcmp(__PRETTY_FUNCTION__)可以在编译时计算(参见第(8)行)。

考虑到(11)(12)的失败,可以得出结论,在某些情况下,__PRETTY_FUNCTION__的行为就好像它被宣布为static constexpr char const [],而在其他情况下则被宣布为static char const []。换句话说,如果__PRETTY_FUNCTION__有一个类型,那么它在编译的所有步骤中都是不一致的(我指的是GCC 8.3)。