constexpr无法针对LLVM编译器正确评估

问题描述

我正在VS编译器和LLVM编译器上编译以下代码,并注意到LLVM编译器无法正确评估constexpr。

因此,下面示例中的以下摘录在LLVM和VS编译器中的行为有所不同

template <TUInt64 TWAppType>
bool
TWApplicationTemplate<TWAppType>::InternalInitialize ()
{
    if constexpr (TWAppClientAsBackgroundThread (TWAppType)) {
        static_assert(0,"TRUE evaluated");
        extern bool TWClientMain ();
    }
    return true;
}

对于LLVM编译器,此表达式(constexpr(TWAppClientAsBackgroundThread(TWAppType))返回true,但对于VS,在编译时返回false(VS行为正确)

//样品代码

#include <iostream>

constexpr int TWApp_Client_As_Background_Thread = 8192;

constexpr bool TWAppClientAsBackgroundThread(int pTWAppType) {
  return ((pTWAppType & TWApp_Client_As_Background_Thread) != 0);
}

typedef unsigned _int64 TUInt64;

template <TUInt64 TWAppType>
class TWApplicationTemplate {
 public:
  static bool TWApplicationInitialize();

 private:
  static bool InternalInitialize();
};

template <TUInt64 TWAppType>
bool TWApplicationTemplate<TWAppType>::TWApplicationInitialize() {
  return InternalInitialize();
}

template <TUInt64 TWAppType>
bool TWApplicationTemplate<TWAppType>::InternalInitialize() {
  if constexpr (TWAppClientAsBackgroundThread(TWAppType)) {
    static_assert(0,"TRUE evaluated");
    extern bool TWClientMain();
  }
  return true;
}

int main(int pArgc,char* pArgv[]) {
  constexpr int TVal = 987080;

  TWApplicationTemplate<TVal> t1;
  t1.TWApplicationInitialize();
  return 0;
}

bool TWClientMain() {
  std::cout << "Executing TWClientMain " << std::endl;
  return true;
}

解决方法

对于LLVM编译器,此表达式(constexpr(TWAppClientAsBackgroundThread(TWAppType))返回true,但对于VS,在编译时返回false(VS行为正确)

但是真的吗?我认为您的假设是不正确的。我认为LLVM和MSVC都评估为false。

然后您会说:

但是...但是... LLVM打印TRUE evaluated,这怎么可能?

这是因为您的代码格式错误。

此行不是有效的C ++:

static_assert(0,"TRUE evaluated");

该标准在[temp.res]/8(强调我的位置)上说:

可以在进行任何实例化之前检查模板的有效性。 [注意:知道哪些名称是类型名称,这样就可以检查每个模板的语法。 — 尾注] 该程序格式不正确,无需进行诊断,如果:

  • 如果模板中的语句或constexpr的子语句未在模板中实例化,则无法为该模板或constexpr的子语句生成有效的专业化,或者
  • [...]

由于无法存在static_assert(0,"...")有效的模板的特殊化,编译器可以发出诊断信息。

并且由于不需要诊断,因此两个编译器都是正确的,即使使用MSVC,它也仍然无法正确构建程序。

如您所见,这非常类似于模板。您可能有一个永远不会实例化且永远不会出现在二进制文件中的模板,但是编译器仍会检查代码的有效性。如果可以提前证明代码格式不正确,则可以发出诊断信息。

如果constexpr的废弃部分也是如此。丢弃的部分没有被编译,但是它不是令牌标记:编译器可以检查该代码是否不是明显的错误形式。如果您调用一个不存在的函数或编写static_assert(false),那么编译器可能会发出诊断,并且这样做是正确的,即使它位于constexpr的废弃部分中。