__func__指针的两个constexpr实例的区别是否仍然是constexpr?


14

这是有效的C ++吗?

int main() {
    constexpr auto sz = __func__ - __func__;
    return sz;
}

GCC和MSVC认为可以,而Clang认为不可以:Compiler Explorer


所有编译器都同意这是可以的:Compiler Explorer

int main() {
    constexpr auto p = __func__;
    constexpr auto p2 = p;
    constexpr auto sz = p2 - p;
    return sz;
}

Clang再次不喜欢这个,但是其他的都可以:Compiler Explorer

int main() {
    constexpr auto p = __func__;
    constexpr auto p2 = __func__;
    constexpr auto sz = p2 - p;
    return sz;
}

这是什么 我认为对不相关的指针进行算术运算是未定义的行为,但是__func__返回相同的指针,不是吗?我不确定,所以我想我可以测试一下。如果我没记错的话,std::equal_to可以比较不相关的指针而没有未定义的行为:

#include <functional>

int main() {
    constexpr std::equal_to<const char*> eq{};
    static_assert(eq(__func__, __func__));
}

Clang认为eq(__func__, __func__)不是常量表达式,即使std::equal_to::operator() 是constexpr。其他编译器不抱怨: 编译器资源管理器


Clang也不会编译这个。抱怨__func__ == __func__不是一个常量表达式:Compiler Explorer

int main() {
    static_assert(__func__ == __func__);
}

Function_definition开始__func__就好像static const char __func__[] = "function-name";并且接受了等效演示 ...
Jarod42 '19

有趣的是,如果您使用constexpr变量初始化__func__并在static_assert中使用该变量,它会起作用...
florestan

@ Jarod42这是Clang中的错误吗?
Ayxan

@florestan像这样吗?它也不会用Clang编译。我在问题中的第二和第三示例就是您提到的方式。一个编译,另一个不编译。
Ayxan

1
另请参见CWG1962,它可能会__func__完全从constexpr评估中删除。
戴维斯·鲱鱼

Answers:


13

__func__在C ++中是一个标识符。特别是,它引用一个特定的对象。来自[dcl.fct.def.general] / 8

_­_­func_­_­定义函数局部的预定义变量就像定义形式一样

static const char __func__[] = "function-name";

已提供,其中function-name是实现定义的字符串。尚不确定此类变量的地址是否不同于程序中其他任何对象的地址。

作为功能局部的预定义变量,此定义(好像)出现在功能块的开头。这样,__func__该块内对的任何使用都将引用该变量。

对于“任何其他对象”部分,变量定义一个对象。__func__命名该变量定义的对象。因此,在一个函数中,所有使用__func__名称的变量都相同。未定义的是该变量是否是与其他对象不同的对象。

也就是说,如果您使用的函数名为foo,并且"foo"在问题中的其他地方使用了文字,那么实现中也不允许变量__func__也与文字"foo"返回的对象相同。也就是说,该标准并不要求__func__出现的每个函数都必须存储与字符串文字本身分离的数据。

现在,C ++的“好像”规则允许实现偏离此实现,但是它们无法以可检测的方式实现。因此,尽管变量本身可能具有或不具有与其他对象不同的地址,但是__func__在同一函数中使用时,必须表现得就像它们在引用同一对象一样。

Clang似乎没有实现__func__这种方式。它似乎实现了它,就好像它返回了函数名称的prvalue字符串文字一样。两个不同的字符串文字不必引用同一对象,因此减去指向它们的指针就是UB。并且在常量表达式上下文中的未定义行为是不正确的。

令我犹豫的是,[lang.arg.nontype] / 2是让Clang在这里100%错误的唯一原因

对于引用或指针类型的非类型模板参数,常量表达式的值不得引用(或对于指针类型,不得为以下地址):

...

  • 预定义_­_­func_­_变量。

看,这似乎允许实现过程有些混乱。也就是说,虽然从__func__技术上讲可以是一个常量表达式,但是您不能在模板参数中使用它。即使从技术上讲它是一个变量,也将其视为字符串文字。

因此,从某种程度上来说,我想说的是该标准是从其口中说出来的。


因此,严格来说__func__,在我所问的所有情况下,它都是一个不变的表达,对吗?因此代码应该已经编译。
Ayxan

那么“不确定该变量的地址是否不同于程序中任何其他对象的地址”。部分?未指定的行为意味着抽象机行为的不确定性。这对constexpr评估会产生问题吗?如果第一次出现__func__的地址与另一个对象的地址相同,而第二次出现的地址不相同,该__func__怎么办?当然,这并不意味着两个实例之间的地址不同,但是我仍然感到困惑!
Johannes Schaub-litb

@ JohannesSchaub-litb:“ “不确定这样的变量的地址是否不同于程序中其他任何对象的地址。”部分呢__func__不是宏;它是一个标识符,它命名一个特定的变量,从而命名一个特定的对象。因此,__func__在同一函数中的任何使用都应导致引用同一对象的glvalue。或更重要的是,它不可能以这种情况来实现。
Nicol Bolas

@Nicol指向相同的对象。但是该对象可能在某一时刻具有与另一对象相同的地址。而在另一瞬间还没有。我并不是说这是一个问题,但我只是在提醒所有人这种可能性。毕竟,我也可能会弄错,所以我也这样说是为了希望得到更正或证实。
Johannes Schaub-litb

@ JohannesSchaub-litb:“ 但是该对象可能在某一时刻与另一个对象具有相同的地址。 ”在C ++对象模型下,这是不允许的。两个对象(一个都不嵌套在另一个对象中)不能同时处于同一存储库中的生命周期内。而且该对象具有静态的存储持续时间,因此,除非new在其上使用放置,否则它不会在程序结束前随处可见。
Nicol Bolas
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.