【问题标题】:std::abs can be used in constexpr function, but only if it's templated. Why?std::abs 可以在 constexpr 函数中使用,但前提是它是模板化的。为什么?
【发布时间】:2020-10-06 06:12:09
【问题描述】:

据说std::abs 在标准中不是constexpr(即使在 C++20 中也是如此)。但在实践中,我发现在函数模板化的非常特殊的条件下,我可以将其编译为constexpr。请参阅这个完整的工作示例:

template<class T>
constexpr T f(const T input) {
   return std::abs(input);
}

int main() {
   int i = -1;
   int a = f(i);
   return 0;
}

代码:

  • 无论有没有模板,都可以使用 GCC 正常编译。
  • 它在 Clang 中不起作用。
  • 在 Visual Studio 中,它使用模板行进行编译,但在没有模板的情况下编译失败。

【问题讨论】:

  • 不,它确实有效!即使在调用时。我已经在不知不觉中使用了这个“技巧”。
  • Clang 抱怨这两种情况。
  • 这能回答你的问题吗? Why isn't abs constexpr?
  • 尝试在 MSVC 中将 constexpr 添加到您的 ia - 它可以编译吗?如果不是,那么f 就不是真正的constexpr 函数。
  • “我的意思是,如果它像那样编译,它仍然是 constexpr” - 不,不是那个意思。如果实例化不是 constexpr,则声明为 constexpr 的模板“回退”为非 constexpr

标签: c++ constexpr function-templates


【解决方案1】:

对于常规函数,编译器可能会根据函数参数的类型知道内部代码是否可以在编译时进行评估。这就是您在 MSVCclang 中调用 std::abs 时收到错误消息的原因。 gcc 的行为是基于其将std::abs 实现为constexpr 的决定,顺便说一下a questionable decision

对于模板函数,编译器无法知道内部代码是否可以在编译时进行评估,因为它可能基于模板参数的实际类型,调用不同的函数重载。虽然大多数编译器会决定不检查是否所有可能的 std::abs 重载都不能是 constexpr,从而让代码通过编译,但理论上编译器可能会检查(在非常特殊的情况下可以检查,比如这个),因为不允许用户通过添加新版本的abs 来扩展std(允许扩展std 的列表已被规范关闭)可以看到该函数永远不会是constexpr 和从而产生编译错误。然而,在更一般的情况下,如果所有可能的情况都无法生成 constexpr 函数,编译器无法检查模板函数,因为每次调用模板函数时,它只看到内部调用的可用重载,并且可能当模板在别处调用时,是内部调用的其他可用重载。


请注意,将constexpr 函数设为模板,以使其能够被编译,这不是一个好方法。函数是否为constexpr(即可以在编译时调用)的实际决定将基于实际调用,如果在所有情况下该函数都不能是constexpr,那么您正在尝试欺骗编译器但是最终主要是在欺骗自己……


顺便说一句,在我检查clang 10.1 和trunk 版本时,我没有收到模板版本的编译错误,此代码compiles both with gcc and clang

template<typename T>
constexpr T myabs(T t) {
    return std::abs(t);
}

int main() {
    int i = myabs(3);
}

虽然这使用 gcc 编译(将 std::abs 实现为 constexpr)并因 clang 而失败:

int main() {
    constexpr int i = myabs(3);
}

似乎 gccclang 都不会产生错误,即使 constexpr 模板函数内的内部调用不依赖于模板参数 @987654323 @:

int myabs() {
    return 42;
}

template<class T>
constexpr int f() {
    // this is never a contexpr
    // yet gcc and clang are ok with it
    return myabs();
}

同样,这是允许的,因为不符合标准的 constexpr 模板函数不需要诊断:

[dcl.constexpr] 9.2.5/7 - The constexpr and consteval specifiers:

[...] 如果模板的特殊化在被视为非模板函数时不能满足 constexpr 函数的要求,则模板格式错误,不需要诊断。

【讨论】:

  • 在第二种情况下,函数模板是格式不正确的 NDR。没有一组模板参数会产生一个实际的 constexpr 函数(直到 std::abs 正式成为 constexpr)。
  • 我一直认为成功编译为 constexpr 意味着它至少在理论上可以在编译时运行?这个假设是错误的吗?
  • @Basti 不适用于模板函数,编译器没有义务检查所有可能的重载并确保它永远不会是 constexpr,在大多数情况下它实际上无法进行检查,因为它只看到每次调用模板函数都有可用的重载,当模板在别处调用时,内部调用可能还有其他可用的重载。很少有可以检查的情况,例如直接调用 std:: 函数时,但编译器又没有义务检查。
  • @StoryTeller-UnslanderMonica 会将其添加到答案中,谢谢。
猜你喜欢
  • 1970-01-01
  • 2018-02-25
  • 2016-11-19
  • 1970-01-01
  • 2021-04-18
  • 1970-01-01
  • 2017-08-07
  • 2020-10-30
  • 1970-01-01
相关资源
最近更新 更多