【问题标题】:Type deduction time类型扣除时间
【发布时间】:2019-06-30 02:28:32
【问题描述】:

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

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&lt;int&gt;Helper&lt;bool&gt;中的2个T是同一个类型,这让我很纳闷:

  1. 为什么标有// ??? 的行只执行一次,而不是每种类型执行一次?
  2. 有没有办法强制对每种类型的行执行一次,最好不修改 Holder 的定义?

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

(更接近)真实的场景是:

  1. struct Holder 是在第三方库的标头中定义的。 struct 的类型实际上很复杂,库编写器为用户提供了另一个宏:
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);
}

这里的问题是下面2个用法的语义不一致,如图所示:

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 一样。

【问题讨论】:

  • 即使它打印 1 并不意味着 Helper&lt;int&gt;Helper&lt;bool&gt; 是同一类型。它们是不同的类型static_assert(false == ::std::is_same_v&lt;Helper&lt;int&gt;, Helper&lt;bool&gt;&gt;);。目前尚不清楚您要在这里实现什么目标。
  • @VTT - OP 询问Helper&lt;bool&gt;::THelper&lt;int&gt;::T - 这里是同一类型。
  • @StoryTeller 是的,但是如果 OP 想要从 Helper&lt;bool&gt;Helper&lt;int&gt; 中获得两种不同的类型,他可以自己使用这些类型。或者制作类似struct T{}; 的东西,而不是using T = Holder&lt;__COUNTER__&gt;;。所以不清楚他为什么需要Holder 类型。
  • @VTT - 我想这是元编程尝试的某种简化形式。我同意这可能是一个 XY 问题。
  • @kkspeed - 与其专注于问题 2,您认为这是解决您实际问题的方法,您介意告诉我们关于您的最终目标的一两件事吗?就像我已经说过的,这有一种XY question 的感觉。

标签: c++ templates c-preprocessor


【解决方案1】:

我不确定我是否完全理解了这个问题,但由于 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;
}

【讨论】:

    【解决方案2】:

    符号__COUNTER__ 是一个预处理宏,它只扩展一次

    这意味着T始终Holder&lt;0&gt;(因为__COUNTER__ 从零开始),无论用于模板Helper 的类型如何。

    参见例如this GCC predefined macro reference 了解更多关于 __COUNTER__ 的信息。

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-02-19
    • 2012-10-30
    • 1970-01-01
    • 1970-01-01
    • 2018-03-11
    • 2015-10-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多