成员函数范围变量

时间:2013-04-26 12:02:53

标签: c++ static scope

请考虑以下代码:

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

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

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

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

Update²:显然(并且不幸的是)确实没有具有此确切语义的正确语言功能。 虽然有一些解决方法,但每个解决方案都会引入限制和陷阱

  • 放置“功能”声明和定义
  • 访问其他“真实”成员变量
  • 在派生类中重载“function”
  • ...

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

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

或类似。

非常感谢您的意见和答案!

9 个答案:

答案 0 :(得分:19)

不,这是不可能的。

您可以使用两种替代选项:

  1. 使变量成为成员,这是具有所需语义的语言特性;
  2. 或发明一种新语言。
  3. 我确实理解您的担忧,但它实际上只源于您class显然太大。分开责任。

答案 1 :(得分:6)

轮到我了:

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

基本上,函数静态变量使函数成为对象(标识,状态,行为)。你只是不希望它是单身人士。所以这是 - 一个班级。

答案 2 :(得分: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++;
    }
};

答案 3 :(得分:2)

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

答案 4 :(得分:1)

fooX的唯一方法吗?看来你只是想要一种方便的方法来创建计数器:

#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';
}

提示:如果您不使用Xfoo等名称,您将获得更好的答案; - )

答案 5 :(得分:0)

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

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

答案 6 :(得分:0)

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

答案 7 :(得分: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++;
    }
};

我不宽恕它,因为我觉得它太可怕了,当你试图复制周围物体时可能会黯然失色,但是你去了。此外,是的,每次销毁一个物体时它都会泄漏一点内存。

在结构中存储值的原因是,您可以确保在std::map中创建新条目时将其初始化为零,这将在调用{{1 }}

这个程序给我foo()作为输出,但我参考上面关于评估顺序的评论。

答案 8 :(得分: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;
};