9

考虑以下代码:

#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而无需将声明和初始化移动到一些遥远的头文件和构造函数?

我想要这个的原因是因为这个变量的值只与这个特定的函数相关(但也特定于它的成员函数的实例),例如,最后一次调用参数、最后一次调用的时间等。

支持这个想法的类已经有点大了,并且在一个函数中使用的这种微小变量的声明和初始化变得越来越难看。

更新:请注意,我不想泄漏内存。当一个实例被销毁时,与之关联的变量也应该被删除。

更新²:显然(不幸的是)确实没有具有这种确切语义的适当语言功能。虽然有一些变通方法,但它们中的每一个都引入了限制和陷阱,当涉及到

  • “函数”声明和定义的位置
  • 访问其他“真实”成员变量
  • 在派生类中重载“函数”
  • ...

考虑到这些含义,坚持想到的第一件事似乎是最有效的:

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

或类似的。

非常感谢您的评论和回答!

4

9 回答 9

19

不,这是不可能的。

您可以使用两种备选方案:

  1. 使变量成为成员,这是具有您想要的语义的语言特征;
  2. 或发明一种新语言。

我确实理解您的担忧,但这实际上只是源于您class显然太大了。分离其职责。

于 2013-04-26T12:06:48.493 回答
6

轮到我了:

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

基本上,函数静态变量使函数成为对象(身份、状态、行为)。你只是不希望它是单例的。所以这里是 - 一个类。

于 2013-04-26T12:26:41.580 回答
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
};

编辑:没有成员变量的替代方案,适用于那些不寻找简单选项的人:

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]++;
    }
};

编辑:与上面相同,但没有构造函数/析构函数:

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++;
    }
};
于 2013-04-26T12:10:07.817 回答
2

您可以将函数状态封装在 lambda 中,包含在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;
}

请注意,这使用了 lambda 通用捕获,这是一个 C++14 功能,但已被 g++ 支持(至少从 4.7.2 开始)。否则,您可以手动将 lambda 重写为(更高效的)仿函数:

#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;
}
于 2013-04-26T13:03:41.017 回答
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;-)

于 2013-04-26T12:15:54.463 回答
0

扩展 Valeri 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++;
    }
};

我不宽恕它,因为我认为这很可怕,并且当您尝试复制对象时可能会摔倒,但是您去了。另外,是的,每次销毁对象时它都会泄漏一点内存。

将值存储在 struct 中的原因是,当您在 中创建一个新条目时,您可以确定它将被初始化为零std::map,这将在每个新对象调用时发生foo()

这个程序给了我0 1 0一个输出,但我参考了我上面关于评估顺序的评论。

于 2013-04-26T13:04:01.797 回答
0

你可以看看Knuth 的Literate Programming。它做了这样的事情。基本上,您需要一种可以让您在文本中移动的宏语言。

于 2013-04-26T12:31:52.203 回答
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;
};
于 2013-04-26T14:02:41.593 回答
0

您可以创建一个GetVariableI(X)接受 X 实例并返回特定于实例的值的方法,或者在传递 null 时返回静态值。

这种方法应该可以解决您的问题,但是它很愚蠢。它做你说你想要的,但几乎可以肯定不是你需要的。你想要达到的行为是什么?如果您可以详细说明您想要实现的目标,我们可以提供一个替代(和理智)的解决方案。

于 2013-04-26T12:19:21.577 回答