【问题标题】:Creating effect of method variable static both per-instantiation and per-enclosing-instance为每个实例化和每个封闭实例创建方法变量静态的效果
【发布时间】:2017-02-20 17:31:52
【问题描述】:

众所周知,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&lt;int&gt;f&lt;float&gt;),因此 .BSS/.DATA 段中有两个 is。


问题

我想要一些方法让模板成员函数中的变量同时存在于它们的封闭类的 per-instanceper-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>();
}

【问题讨论】:

  • 能否请任何反对者给出理由?对我来说似乎是一个写得很好的问题,尽管可能是一个 XY 问题......
  • 我知道为什么这里有两个接近投票。这个问题很清楚(虽然肯定很难回答)。

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


【解决方案1】:

让对象的不同实例为您提供不同结果的唯一方法是让这些对象具有具有不同值的成员变量。最简单的方法就是拥有std::mapstd::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() 作为哈希。

【讨论】:

    【解决方案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
    }
    

    【讨论】:

    • 这个问题的所有当前答案基本上都采用这种方法,但有一些变化。接受这个版本,因为我认为它是最干净的。
    【解决方案3】:

    通常应该避免使用typeid,另一种可能是变量模板。当然,变量模板可能只是静态的,因此要为每个实例存储唯一值,需要变量模板映射instance -&gt; 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::unordered_map 替换std::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]

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-05-29
      • 2020-07-27
      • 1970-01-01
      • 2015-07-24
      • 2012-09-29
      • 1970-01-01
      相关资源
      最近更新 更多