在per-instantiation和per-enclosing-instance

时间:2017-02-20 17:31:52

标签: c++ static instance c++14

众所周知,例程,函数或方法的static变量("成员函数")exist for each unique instantiation

(快速审查和健全检查。)

在通常情况下,这只是一个变量:

int f() {
    static int i = 0;
    return i++;
}

也就是说,有一个变量icreated 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>();
}

3 个答案:

答案 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.TypeIndexTemplate 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);
}

[live demo]

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

[live demo]