kkspeed
kkspeed

Reputation: 403

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.

Upvotes: 8

Views: 577

Answers (2)

Helmut Zeisel
Helmut Zeisel

Reputation: 406

I am not sure whether I completely understand the problem, but since C++14 there is no need to use DEF_HOLDER two times. The following code also works:

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

If you want a different type for every function call, you can add the int parameter:

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

You could hide this int in a macro:

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

Then a,b and c,d have the same semantics:

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

Upvotes: 0

Some programmer dude
Some programmer dude

Reputation: 409166

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__.

Upvotes: 13

Related Questions