繁体   English   中英

成员函数范围变量

[英]Member-Function-Scoped Variable

请考虑以下代码:

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

是否有可能为每个X实例获取此静态变量i的副本,而无需将声明和初始化移动到一些远距离的头和构造函数?

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

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

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

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

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

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

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

或类似的。

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

不,这是不可能的。

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

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

我确实理解你的担忧,但它真的只是因为你的class显然太大了 分开责任。

轮到我了:

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

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

可能这就是你想要的:

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

您可以将函数状态封装在一个包含在std::function成员中的lambda中:

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

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这样的名字,你会得到更好的答案;-)

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

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

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

对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创建新条目时它将初始化为零,这将在调用foo()为每个新对象进行。

这个程序给我0 1 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;
};

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM