constexpr 中允许未定义的行为——编译器错误?

Dan*_*ury 6 c++ undefined-behavior constexpr

我的理解是:

  • C++ 中的有符号整数溢出是未定义的行为
  • 常量表达式不允许包含未定义的行为。

看来像下面这样的东西不应该编译,事实上在我的编译器上它不编译。

template<int n> struct S { };

template<int a, int b>
S<a * b> f()
{
  return S<a * b>();
}

int main(int, char **)
{
  f<50000, 49999>();
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

但是,现在我尝试以下方法:

#include <numeric>

template<int n> struct S { };

template<int a, int b>
S<std::lcm(a, b)> g()
{
  return S<std::lcm(a,b)>();
}

int main(int, char **)
{
  g<50000, 49999>();
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

g++、clang 和 MSVC 都会愉快地编译它,尽管事实上

如果 |m|、|n| 或 |m| 的最小公倍数,则行为未定义 和|n| 不能表示为 type 的值 std::common_type_t<M, N>

来源: https: //en.cppreference.com/w/cpp/numeric/lcm

这是所有三个编译器中的错误吗?或者,如果 cppreference 无法表示结果,则 cppreference 是否认为 lcm 的行为未定义是错误的?

Bri*_*ian 12

根据[expr.const]/5,在常量求值期间不允许“具有 [intro] 到 [cpp] 中指定的未定义行为的操作”,但是:

\n
\n

如果E满足核心常量表达式的约束,但对E的求值将求出具有 [library] 到 [thread] 中指定的未定义行为的操作,或者宏调用va_\xc2\xadstart([cstdarg.syn]),则它是未指定E是否是核心常量表达式。

\n
\n

我们通常将其总结为“语言UB必须在需要恒定表达的上下文中进行诊断,但库UB不一定需要进行诊断”。

\n

这条规则的原因是,导致库 UB 的操作可能会也可能不会导致语言 UB,并且即使在它不导致语言 UB 的情况下,编译器也很难一致地诊断库 UB。(事实上​​,即使是某些形式的语言 UB 也不能通过当前的实现来一致诊断。)

\n

有些人还将语言 UB 称为“硬”UB,将库 UB 称为“软”UB,但我不喜欢这个术语,因为(在我看来)它鼓励用户思考“它的代码”未指定语言 UB 是否出现”在某种程度上比“明确具有语言 UB 的代码”要好。但在这两种情况下,结果都是程序员无法编写执行此类代码的程序并期望任何东西都能正常工作。

\n