2

My question is different from Static variables in member functions.

class A {
public:
    void foo() {
        B b(params_); // It takes lots of time to construct b. b is only used in foo().
        // do something with b
        return;
    }
private:
    int params_;
};

int main() {
    A a1(params1), a2(params2);
    a1.foo(); // call 1
    a1.foo(); // call 2
    a2.foo(); // call 3
}

I want b to be same in call 1 & call 2 and constructed only once and should be seen in foo() only. However b should have different value in call 2 & call 3 due to different value of params_ in a1 and a2. How should I declare b?

  1. static variable in foo(): b has same value across different instances a1, a2. It doesn't meet my requirement.
  2. local variable in foo(): b is constructed every time foo() is called. It doesn't meet my requirement.
  3. member variable of class A: b can be seen by other member functions of A. It doesn't meet my requirement.
  4. other good choice?
Jabberwocky
  • 48,281
  • 17
  • 65
  • 115
  • 2
    My first impression is that your class is trying to do too many things. Rather than trying to meet your requirements for `foo`, I would rethink your design for `A`. (However, deciding if that is the right approach would require more information in the question than is warranted by the question.) – JaMiT Mar 25 '22 at 09:59
  • 3
    I have the impression you're stuck with option 3. What you want is some kind of super private member that can only bee seen from `A::foo`, but C++ doesn't have this. – Jabberwocky Mar 25 '22 at 10:03
  • why is 3 not ok? There are no other member functions. If there are other member functions then I agree with Jabberwocky, most likely the other members function do belong in a different class. What you describe is exactly what member variables are for – 463035818_is_not_an_ai Mar 25 '22 at 11:36
  • Pass as a parameter (const reference?) to `foo`? Maintain something like a `std::pair` at the calling site? – Bathsheba Mar 25 '22 at 12:10
  • @463035818_is_not_a_number yes, there are foo2, foo3... in real case, and each of these functions may be maintained by different people. – Euphoria Yang Mar 26 '22 at 05:42
  • @Bathsheba If I pass b to `foo`, then b can be seen/used at caller. Also, b must be created correctly at caller. But I can't access the caller of `foo` . – Euphoria Yang Mar 26 '22 at 05:59
  • @JaMiT Actually, `B` is `cv::Mat` or `std::vector` and required lots of time to compute the value of every elements. This computation can be done in `A`'s constructor if I declare `b` as member variable of `A`. However, there are many variables play same role as `b` in `foo2`, `foo3`. It will be messy if I put all these variables to member variables. – Euphoria Yang Mar 26 '22 at 06:12
  • 1
    @EuphoriaYang That reinforces my impression that you should rethink your design for `A`. – JaMiT Mar 26 '22 at 06:23

1 Answers1

0

You can try this, but I think that it's an ugly trick:

#include <iostream>
#include <map>
#include <memory>

class B {
public:
    B(int src) {
        std::cout << "B constructed from " << src << "." << std::endl ;
    }
    void foo(int i) {
        std::cout << "B.foo(" << i << ") called." << std::endl ;
    }
} ;

class A {
public:
    int inst ;
    A(int v) : inst(v) {}

    void foo() {
        // Static map associating current A instance and a B object.
        static std::map<A*,std::unique_ptr<B>> Bmap ;
        // Search an already existing instance.
        auto b = Bmap.find(this) ;
        if (b==Bmap.end())
            // Not found, create one.
            b = Bmap.insert({this,std::make_unique<B>(B(inst))}).first ;
        // If already an instance of B, returns it, otherwise create it with params_
        // Do something with b.
        b->second->foo(inst) ;
        return;
    }
} ;

int main() {

   A a1(1), a2(2) ;

   a1.foo() ;
   a2.foo() ;
   a1.foo() ;
}

This code compiles and run, it produces that output:

B constructed from 1.
B.foo(1) called.
B constructed from 2.
B.foo(2) called.
B.foo(1) called.

But a member variable would be cleaner and better, even if you use just-in-time initialization and leaves it to nullptr until you really need it.

Wisblade
  • 1,483
  • 4
  • 13
  • This has potential (it's pretty elegant) but has two issues (1) multiple creation of `B` and the OP has already told us that's slow (2) it's not thread safe. – Bathsheba Mar 25 '22 at 12:13
  • @JaMiT You know that most people don't take care of the standard they use, until you tell them to look at it... Sad but true. For `try_emplace`, C++ reference says that **IF** the element already exists, nothing is done but returning the existing element. Again, I didn't ran the code, too small and fragmented. It may lacks a `*` somewhere, the purpose was to show how it can be done. OP's code can't even compile. – Wisblade Mar 25 '22 at 12:17
  • @Bathsheba Don't care about thread safety, word isn't even used in OP and a guard can always solve that. I already said that I didn't ran the code, I just read the doc for `try_emplace` but didn't tried that directly. I also gave the "long" solution with `find`/`insert`. – Wisblade Mar 25 '22 at 12:24
  • @JaMiT Then go for `find`+`insert`. – Wisblade Mar 25 '22 at 12:49
  • My downvote remains until the concurrency issues are dealt with. – Bathsheba Mar 26 '22 at 12:12