簡體   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