类型推导时间

时间:2019-02-06 07:23:43

标签: c++ templates c-preprocessor

我今天早些时候遇到了这个问题。在以下代码中:

template <int> struct Holder {};

template <typename> struct Helper { using T = Holder<__COUNTER__>; };  // ???

int main() {
  auto a = typename Helper<bool>::T();
  auto b = typename Helper<int>::T();

  std::cout << (typeid(a) == typeid(b)) << std::endl;
  return 0;
}

使用以下命令编译和执行时:

g++ test.cpp -std=c++11 -o test
./test

它打印出1而不是0,这意味着Helper<int>Helper<bool>中的2个Ts是同一类型,这使我感到奇怪:

  1. 为什么标记为// ???的行只执行一次,而不是每种类型都执行一次?
  2. 是否有一种方法可以强制对每种类型的行执行一次,并且最好不修改Holder的定义?

================================================ ===== 说明:

(更接近)真实的情况是:

  1. struct Holder在第三方库的标头中定义。结构的类型实际上非常复杂,并且库编写器为用户提供了另一个宏:
template <bool, int> struct Holder {};

#define DEF_HOLDER(b)  Holder<b, __COUNTER__>()

在程序的某个时刻,我想通过为类型加上别名来使用当前计数器获取该类型的“快照”,以便可以在函数中使用它:

template <bool b>
struct Helper { using T = decltype(DEF_HOLDER(b)); };

template <bool b, typename R = typename Helper<b>::T>
R Func() {
  return R();
}

// Note that the following does not work:
// Since the 2 types generated by DEF_HOLDER do not match.
template <bool b>
auto Func() -> decltype(DEF_HOLDER(b)) {
  return DEF_HOLDER(b);
}

这里的问题是以下两种用法的语义不一致,如图所示:

int main() {
  auto a = DEF_HOLDER(true);
  auto b = DEF_HOLDER(true);
  auto c = Func<true>();
  auto d = Func<true>();

  std::cout << (typeid(a) == typeid(b)) << std::endl;  // prints 0
  std::cout << (typeid(c) == typeid(d)) << std::endl;  // prints 1

  return 0;
}

在我的用例中,与直接调用Func一样,多次调用DEF_HOLDER返回不同的类型很重要。

2 个答案:

答案 0 :(得分:13)

符号__COUNTER__预处理器宏,仅扩展一次

这意味着T总是 Holder<0>(因为__COUNTER__从零开始),无论模板Helper使用哪种类型

例如参见this GCC predefined macro reference以获得有关__COUNTER__的更多信息。

答案 1 :(得分:0)

我不确定我是否完全理解该问题,但是由于C ++ 14,因此不需要两次使用DEF_HOLDER。以下代码也适用:

template <bool b>
auto Func() {
   return DEF_HOLDER(b);
}

如果您希望每个函数调用都使用不同的类型,则可以添加int参数:

template <bool b, int i>
auto Func()
{
  return Holder<b, i>();
}

您可以将此int隐藏在宏中:

#define FUNC(b)  Func<b,__COUNTER__>();

那么a,b和c,d具有相同的语义:

int main() {
  auto a = DEF_HOLDER(true);
  auto b = DEF_HOLDER(true);
  auto c = FUNC(true);
  auto d = FUNC(true);

  std::cout << (typeid(a) == typeid(b)) << std::endl;  // prints 0
  std::cout << (typeid(c) == typeid(d)) << std::endl;  // prints 0

  return 0;
}