有状态元编程中使用的模板 function/class 隐式实例化规则

Template function/class implicit instantiation rules used in stateful meta programming

我正在处理有关有状态元编程的代码示例。 原始代码示例可以从这个link中找到:http://b.atch.se/posts/non-constant-constant-expressions/#appendix-clang-workaround

为了理解这个技巧,我试着每次都稍微修改一下代码示例。以下是仍然有效的最新版本:

constexpr int adl_flag(int);

template <class Tag> struct writer {
  friend constexpr int adl_flag(int) {
    return 0;
  }
};

template <int = adl_flag(0)> constexpr bool is_flag_usable(int) {
  return true;
}

constexpr bool is_flag_usable (...) {
  return false;
}

template <
  class Tag = int,
  bool    B = is_flag_usable(0),
  int       = sizeof (writer<Tag>) // replace Tag with int
>
constexpr int f() {
  return B;
}

int main() {
  constexpr int a = f();
  constexpr int b = f();

  static_assert(a != b, "fail");

  return 0;
}

对我来说,用 int 替换 Tag 似乎微不足道。但是这个替换实际上使 static_assert fail.

我想这是因为编译器不再为第二次调用隐式实例化 f :

constexpr int b = f();

但是模板类型参数Tag似乎与此无关。请有人解释一下这里到底发生了什么。

我使用的编译器是g++ 5.4.1。使用 -std=gnu++14.

编译

sizeof(writer<int>) 是非依赖的。这意味着 wrapper<int> 在第一阶段查找中找到,当 f 被解析(一次)时。

另一方面,

sizeof(writer<Tag>) 依赖于 Tag。因此,它的查找被推迟到第二阶段,当 f 被实例化时(在它的每个调用站点)。