Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Type deduction time

I ran into this problem earlier today. In the following code:

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;
}

When compiled and executed with:

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

It prints out 1 instead of 0, meaning that the 2 Ts in Helper<int> and Helper<bool> are the same type, which makes me wonder:

  1. Why the line marked with // ??? is executed only once instead of once for each of the type?
  2. Is there a way to force the line to be executed once for each of the type and preferably without modifying the definition of Holder?

==================================================== Clarifications:

The (closer to) real scenario is:

  1. The struct Holder is defined in a header from a third-party library. The type for the struct is actually very complicated and the library writer provides users with another macro:
template <bool, int> struct Holder {};

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

At some point of the program, I want to take a "snapshot" of the type with current counter by aliasing the type so that it could be used in a function:

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);
}

The problem here is that the following 2 usage has inconsistent semantics as illustrated:

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;
}

In my use case, it is important for multiple invocation of Func to return different types as it does with invoking DEF_HOLDER directly.

like image 595
kkspeed Avatar asked Feb 06 '19 07:02

kkspeed


1 Answers

The symbol __COUNTER__ is a preprocessor macro, it's expanded once only.

That means T will always be Holder<0> (since __COUNTER__ starts at zero), no matter the type used for the template Helper.

See e.g. this GCC predefined macro reference for more information about __COUNTER__.

like image 79
Some programmer dude Avatar answered Sep 19 '22 04:09

Some programmer dude