众所周知,例程,函数或方法的static
变量("成员函数")exist for each unique instantiation。
在通常情况下,这只是一个变量:
int f() {
static int i = 0;
return i++;
}
也就是说,有一个变量i
是created in .BSS/.DATA"属于"到函数f
。对于模板,它是每个唯一实例化的一个:
template <typename T> int f() {
static int i = 0;
return i++;
}
int g() {
return f<int>() + f<int>() + f<int>() + f<float>();
}
此处有两个唯一的实例化(f<int>
和f<float>
),因此.BSS / .DATA段中有两个i
。
我想要一些方法让模板成员函数中的变量同时存在于其封闭类的 per-instance 和 per-instantiation 。我有兴趣通过或多或少的任何必要的性能手段来实现这种效果(可能根本不涉及静态)。我该怎么做?
例如:
class Foo { public:
template <typename T> int f() {
static int i = 0;
return i++;
}
};
void g() {
Foo foo1;
Foo foo2;
/*
These will have values a=0, b=0, c=1. This happens because there are two
variables:
"Foo::f<float>()::i"
"Foo::f<double>()::i"
What I *want* is for there to be three variables:
"Foo::f<float>()::i" (foo1's copy)
"Foo::f<float>()::i" (foo2's copy)
"Foo::f<double>()::i" (foo1's copy)
So the result is a=0, b=0, c=0. How can I accomplish this effect (maybe
not using static)?
*/
int a = foo1.f<float>();
int b = foo1.f<double>();
int c = foo2.f<float>();
}
答案 0 :(得分:3)
让对象的不同实例给出不同结果的唯一方法是让这些对象具有不同值的成员变量。最简单的方法就是std::map
std::type_info
:
struct TypeInfoCompare {
bool operator()(std::type_info const* lhs, std::type_info const* rhs) const {
return lhs->before(*rhs);
}
};
struct Foo {
template <class T>
int f() {
return m[&typeid(T)]++;
}
std::map<std::type_info const*, int, TypeInfoCompare> m;
};
这为您提供了每个类型的计数器,对于Foo
的每个实例都是不同的。
std::map
也可以是std::unordered_map
,并使用std::type_info::hash_code()
作为哈希。
答案 1 :(得分:2)
您可以为每个实例存储类型映射。在这里,我假设RTTI已启用type_index
,如果您无法使用RTTI,请查看Boost.TypeIndex或Template metaprogram converting type to unique number进行替换。
#include <unordered_map>
#include <typeindex>
#include <cstdio>
class Foo {
std::unordered_map<std::type_index, int> _values;
public:
template<typename T>
int f() {
int& i = _values[typeid(T)];
return i++;
}
};
int main() {
Foo foo1;
Foo foo2;
int a = foo1.f<float>();
int b = foo1.f<double>();
int c = foo2.f<float>();
foo1.f<float>();
int d = foo1.f<float>();
int e = foo1.f<double>();
int f = foo2.f<float>();
printf("%d %d %d %d %d %d\n", a, b, c, d, e, f);
// prints 0 0 0 2 1 1
}
答案 2 :(得分:1)
一般来说,应该避免使用typeid
替代可能是变量模板。当然,变量模板可能只是静态的,因此要为每个实例存储唯一值,需要变量模板映射instance -> value
。
#include <cassert>
#include <map>
struct Foo {
template <class T> static std::map<Foo *, int> m;
template <class T> int f() {
return m<T>[this]++;
}
};
template <class T> std::map<Foo *, int> Foo::m;
int main() {
Foo foo1;
Foo foo2;
assert(foo1.f<int>() == 0);
assert(foo1.f<int>() == 1);
assert(foo1.f<int>() == 2);
assert(foo1.f<float>() == 0);
assert(foo2.f<int>() == 0);
assert(foo2.f<int>() == 1);
assert(foo2.f<int>() == 2);
assert(foo2.f<float>() == 0);
}
将std::map
替换为std::unordered_map
也可能是一个好主意(它不需要任何额外的哈希 - example)
方法也可以在c ++ 14之前成功应用,因为变量模板可以简单地替换为inner structure template。
警告强>
必须指出的是,默认情况下,一种方法不支持给定实例的深拷贝克隆值。
要解决这个问题,可以使用一点点修改后的技术仍然使用变量模板和映射(仍然不需要RTTI):
#include <cassert>
#include <unordered_map>
struct Foo {
template <class T>
static int label;
std::unordered_map<int *, int> m;
template <class T> int f() {
return m[&label<T>]++;
}
};
template <class T> int Foo::label;
int main() {
Foo foo1;
Foo foo2;
assert(foo1.f<int>() == 0);
assert(foo1.f<int>() == 1);
assert(foo1.f<int>() == 2);
assert(foo1.f<float>() == 0);
assert(foo2.f<int>() == 0);
assert(foo2.f<int>() == 1);
assert(foo2.f<int>() == 2);
assert(foo2.f<float>() == 0);
}