2013-04-26 2 views
9

Рассмотрим следующий код:Член-Function-Scoped Переменная

#include <iostream> 

struct X { 
    int foo() { 
     // Can I get this to be an instance-specific static variable, please? 
     static int i = 0; 
     return i++; 
    } 
}; 

int main() { 
    X a, b; 
    std::cout << a.foo() << ' '; 
    std::cout << b.foo() << ' '; 
    std::cout << b.foo() << '\n'; 
    // output is: 0 1 2 
    // desired output: 0 0 1 
} 

Можно ли получить копию этой статической переменной i для каждого экземпляра X без необходимости перемещать объявление и инициализацию некоторых пор , далекие заголовки и конструкторы?

Причина, по которой я хочу это, состоит в том, что значение этой переменной относится только к этой конкретной функции (но также относится к экземпляру, чья функция-член), например, параметры последнего вызова, время последнего вызова и т. Д.

Класс, который стоит за этой идеей, уже несколько велик и распространяется декларация, а инициализация таких крошечных переменных, которые используются в рамках одной функции, становится уродливой.

Обновление: Обратите внимание, что я не хочу утечки памяти. Когда экземпляр уничтожен, связанные с ним переменные также должны быть удалены.

Обновление2: Очевидно, что в этой точной семантике нет надлежащей языковой функции. Хотя есть некоторые обходные пути, каждый из них вводит ограничения и недостатки, когда дело доходит до

  • размещения «функция» декларации и определения
  • обращающихся другие «реальные» переменных членов
  • перегрузки «функции» в производном классе
  • ...

с помощью этих последствий в виду, что это только представляется наиболее эффективным придерживаться первое, что приходит на ум:

struct A { 
    int j = 0; 
    int i = 0; 
    int foo() { return i++ + j++; } 
}; 

вместо того, что-то вроде этого:

struct B { 
    int j = 0; 
    std::function<int()> foo = 
     [this, i = 0]() mutable { return i++ + this->j++; }; 
}; 

или это:

struct C { 
    int j; 
    struct Foo { 
     int i; 
     C *c; 
     Foo(C *c) : i(), c(c) {} 
     int operator()() { return i++ + c->j++; } 
    } foo; 
    C() : j(), foo(this) {} 
}; 

или это:

struct D { 
    int j = 0; 
    std::map<std::string, int> i; 
    int foo() { return i[__PRETTY_FUNCTION__]++ + j++; } 
}; 

или аналогичный.

Огромное спасибо за ваши комментарии и ответы!

+3

так вам нужна статическая переменная, специфичная для экземпляра? Разве это не нарушает цель «статического» в том случае, если она используется внутри класса/структуры? –

+0

@TonyTheLion Но этот 'static' не используется внутри класса, он используется внутри функции. – Angew

+2

Нет, вы не можете этого сделать, вы должны сделать его членом класса. Полная остановка. – syam

ответ

19

Нет, это невозможно.

Два альтернативных варианта доступны для Вас:

  1. Сделать переменной членом, который является языком функция с семантикой вы хотите;
  2. или изобретать новый язык.

Я понимаю ваше беспокойство, но это действительно только проистекает из вашей class, видимо, будучи слишком большой. Отделите свои обязанности.

+12

Пойду с опцией 2 –

+1

@AlvinWong Это C++, что делает JDK? – Malcolm

+0

@ Malcolm Думаю, я должен сказать gcc, но ...? (мой мозг болит) –

4

Вероятно, это то, что вы хотите:

struct X { 
    X() : i(0) {} // initialize variable on construction 

    int foo() { 
     // Can I get this to be an instance-static variable, please? 
     return i++; 
    } 

    int i; // instance variable 
}; 

Edit: Alternative без переменной-члена, для тех, кто не ищет простых вариантов:

typedef std::map<X*,int> XMap; 
static XMap xMap; 

struct X { 
    X() { xMap.insert(this, 0); } 
    ~X() { xMap.erase(this); } 

    int foo() { 
     return xMap[this]++; 
    } 
}; 

Edit: так же, как и выше, но без конструктора/деструктора:

struct X { 
    int foo() { 
     return xMap[this]++; // same as below: 
     // XMap::iterator it = xMap.find(this); 
     // if (it == xMap.end()) 
     // { 
     //  it = xMap.insert(XMap::value_type(this, 0)).first; 
     // } 
     // return *it++; 
    } 
}; 
+0

Это дает желаемый результат, но это не совсем то, что я хочу. В самом деле, это именно то, чего я не хочу. – moooeeeep

+0

@moooeeeep Но это единственный способ. Подумайте, как компилятор узнает об этом: Где выделить пространство для переменной? Какой из них принадлежит определенному моменту? Единственный способ - сделать его членом экземпляра. – Angew

+0

Да, возможно, я ищу синтаксический сахар для этой точной вещи. – moooeeeep

1

Является foo единственным методом X? Похоже, вы просто хотите удобный способ создать счетчики затем:

#include <functional> 

std::function<int()> create_counter() 
{ 
    int i = 0; 
    return [=]() mutable { return i++; }; 
} 

#include <iostream> 

int main() 
{ 
    auto a = create_counter(); 
    auto b = create_counter(); 

    std::cout << a() << '\n'; 
    std::cout << a() << '\n'; 
    std::cout << b() << '\n'; 
    std::cout << b() << '\n'; 
    std::cout << b() << '\n'; 
    std::cout << b() << '\n'; 
    std::cout << a() << '\n'; 
} 

Подсказка: Вы получите намного лучшие ответы, если вы воздерживались от использования имен, таких как X и foo ;-)

+0

Это выглядит интересно - мой пример, конечно, сильно упрощен. В частности, я хочу отслеживать последний параметр вызова и время последнего вызова (тогда можно вычислить продолжительность между двумя вызовами). – moooeeeep

0

Вы можете создать метод GetVariableI(X), который принимает экземпляр X и возвращает значение, специфичное для экземпляра, или статическое значение, когда передается значение null.

Этот подход должен решить вашу проблему, однако это глупо. Он делает то, что вы говорите, вы хотите, но почти наверняка не то, что вам нужно. Какое поведение вы пытаетесь достичь? Если бы вы могли уточнить, чего вы пытаетесь достичь, мы могли бы предложить альтернативное (и разумное) решение.

6

Мой поворот:

struct X { 
    class _foo { 
     int i; 
    public: 
     _foo() : i(0) { } 
     int operator()(void) { 
      return i++; 
     } 
    } foo; 
}; 

В основном, функция-статическая переменная делает функцию объекта (идентичность, состояние, поведение). Вы просто не хотите, чтобы он был одиночным. Итак, вот он - класс.

+0

Не будет ли каждый экземпляр иметь другую версию 'foo'? Сделайте его «статическим», и это будет сжатая версия моего ответа. –

+0

Вы должны смотреть дважды, но по крайней мере инициализация немного ближе под рукой :) – moooeeeep

+0

@PeterWood Я хочу, чтобы каждый экземпляр получал другой экземпляр foo. – moooeeeep

0

Вы можете посмотреть Literate Programming от Knuth. Это было так. В основном вам нужен макроязык, который позволяет перемещаться по тексту.

2

Вы можете инкапсулировать состояние функции в лямбда, содержащегося в std::function члена:

#include <functional> 
#include <iostream> 
struct X { 
    std::function<int()> foo = [i = 0]() mutable { return i++; }; 
}; 
int main() { 
    X a, b; 
    std::cout << a.foo() << " " << b.foo() << " " << b.foo() << std::endl; 
} 

Обратите внимание, что это использует лямбда-обобщенный захват, C++ 14 функция а, но уже при поддержке г ++ (с 4.7. 2 по крайней мере). В противном случае, вы можете вручную переписать лямбда в (более эффективный) функтор:

#include <iostream> 
struct X { 
    struct { int i = 0; int operator()() { return i++; } } foo; 
}; 
int main() { 
    X a, b; 
    std::cout << a.foo() << " " << b.foo() << " " << b.foo() << std::endl; 
} 
+0

Но это все еще член. –

+1

@LightnessRacesinOrbit, он инкапсулирован внутри вызываемого элемента, что улучшает текстовую локальность. – ecatmur

0

Расширение и слегка запутывание, ответа Валерия Atamaniouk, это, кажется, делает трюк без необходимости каких-либо переменных-членов.

struct X { 
    int foo() { 
     struct l_int { 
      int i; 
      l_int():i(0) {}; 
     }; 
     static std::map<X*,l_int> i_map; 
     return (i_map[this]).i++; 
    } 
}; 

Я не оправдываю его, как я думаю, что это ужасно, и, вероятно, упадет, когда вы пытались копировать объекты вокруг, но там вы идете. Кроме того, да, это утечка небольшой памяти каждый раз, когда вы уничтожаете объект.

Причина сохранения значения в структуре заключается в том, что вы можете быть уверены, что она будет инициализирована нолем при создании новой записи в std::map, которая будет происходить для каждого нового объекта при вызове foo().

Эта программа дает мне 0 1 0 в качестве выхода, но я ссылаюсь на свой комментарий выше о порядке оценки.

+0

К сожалению, это приведет к утечке памяти, когда я уничтожу свой X, когда покончу с этим. – moooeeeep

+0

Эй, я никогда не говорил, что это было прекрасно :) – icabod

0

Вот еще один вариант. Не очень чисто концептуально, но она справиться с технической проблемой сдачи вашей функции и переменную ближе друг к другу, оставаясь довольно просто:

struct XFoo { 
    XFoo() : i(0) { } 

    int foo() { return i++; } 

    private: 
     int i; 
}; 

struct X : XFoo { 
    // lots of stuff here 
}; 

или с C++ 11:

struct XFoo { 
    int foo() { return i++; } 

    private: 
     int i = 0; 
};