2017-02-20 49 views
2

衆所周知,例程,函數或方法(「成員函數」)的變量static創建每個實例化和每個包含實例的方法變量靜態效果


(快速回顧和完整性檢查)

在通常情況下,這正是一個變量:

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>),所以有兩個i S IN的.BSS/.DATA段。


問題

我想一些方法,使模板成員函數的變量都住每實例每個實例其封閉類的。我有興趣實現這種效果,或多或少需要任何性能手段(可能根本不涉及靜態)。我應該怎麼做?

例如:

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>(); 
} 
+2

能夠在任何downvoters的請給一個原因?似乎是一個寫得很好的問題,雖然可能是一個XY問題... – ildjarn

+0

Idk爲什麼這裏有兩個密切的選票。這個問題非常清楚(儘管肯定很難回答)。 –

回答

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 
} 
+0

@ildjarn謝謝,編輯。 – kennytm

+0

所有這個問題的當前答案基本上採取這種方法,有一些變化。接受這個版本,因爲我認爲它是最乾淨的。 – imallett

3

只有這樣,纔能有一個對象的不同實例,以提供不同的結果是有這些對象具有不同的值的成員變量。最簡單的方法就是有std::type_infostd::map

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

正如通常應該避免使用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::mapstd::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]