Reputation: 403
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:
// ???
is executed only once instead of once for each of the type?==================================================== Clarifications:
The (closer to) real scenario is:
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
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
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