2017-02-20 38 views
2

Хорошо известно, что переменные функции, функции или метода («функция-член») .Создание эффекта переменной метода static как для каждого экземпляра, так и для каждого экземпляра-экземпляра


(. Краткий обзор и проверка исправности)

В общем случае, это точно одна переменная:

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>), и таким образом, есть два i s в сегменте .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.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 
} 
+0

@ildjarn Спасибо, отредактирован. – kennytm

+0

Все текущие ответы на этот вопрос берут по существу этот подход с несколькими вариантами. Принимая эту версию, поскольку я думаю, что она самая чистая. – imallett

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.


The 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::map с std::unordered_map (это не потребует каких-либо дополнительных Hasher - 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]

 Смежные вопросы

  • Нет связанных вопросов^_^