我今天早些时候遇到了这个问题。在以下代码中:
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是同一类型,这使我感到奇怪:
// ???
的行只执行一次,而不是每种类型都执行一次?================================================ ===== 说明:
(更接近)真实的情况是:
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
返回不同的类型很重要。
答案 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;
}