1

MCVE

#include <map>
class A{
public:
    A(int){

    }
};

class B : public A{
public:
    B()
     : A(filter()){}
    int filter(){
        std::map<int,int> aStuff;
        //new(&m_aStuff)std::map<int,int>;
        m_aStuff = aStuff;
        return 0;
    }
private:
    std::map<int,int> m_aStuff;
};

int main(){
    B b;
    return 0;
}

This fails at compile time because m_aStuff is not initialized. Using answer I added

new(&m_aStuff)std::map<int,int>;

If you uncomment that line it runs at compile time but m_aStuff gets re-initialized anyway when you leave the filter class.

turoni
  • 1,345
  • 1
  • 18
  • 37
  • 3
    Can you show this non working code so we can have a better idea what you want to accomplish? – NathanOliver Jun 06 '17 at 15:58
  • You are correct that you can’t work with uninitialized variables this way, but there’s another issue. Self-move probably doesn’t do what you want; `m_aStuff = std::move(m_aStuff);` probably leaves `m_aStuff` in a valid but otherwise unspecified state. – Daniel H Jun 06 '17 at 15:59
  • What's the relationship between `bar` and `A`? The types of `b` and `a` are somewhat confusing. – Angew is no longer proud of SO Jun 06 '17 at 15:59
  • I don't mind giving the original code but I tried to boil it down to it's essential code. Everything that's not shown works fine. – turoni Jun 06 '17 at 16:13
  • @turoni crafting a [MCVE](http://stackoverflow.com/help/mcve) would be a nice thing to do. – yeputons Jun 06 '17 at 16:15
  • @yeputons there you go, of course this is strange code but it is the boiled down minimum of what I want to do and what goes wrong. – turoni Jun 06 '17 at 16:28
  • 1
    This seems like a case of the [XY problem](https://meta.stackexchange.com/a/233676/218910). Take a step back and explain *why* you think you need to do this. – cdhowie Jun 06 '17 at 16:39
  • So basically you want to initialize field of a child class before initializing parent class? You cannot do that. What you can do, though, is use composition instead of inheritance. – yeputons Jun 06 '17 at 16:45
  • http://cpp.sh/8zuiw It works, what is your error message and compiler? – Teivaz Jun 06 '17 at 17:25
  • g++, just "stopped working" at runtime – turoni Jun 06 '17 at 17:27

2 Answers2

1

I would strongly recommend you to reconsider the design, but here is my solution:

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

class B : public A {
public:
  struct Dirty {
    std::map<int, int> map;
  };

  B(Dirty dirt = Dirty()) : A(filter(dirt)), m_aStuff(std::move(dirt.map)) {}
  int filter(Dirty& dirt) {
    std::map<int, int> aStuff;
    //new(&m_aStuff)std::map<int,int>;
    dirt.map = aStuff;
    return 0;
  }

private:
  std::map<int, int> m_aStuff;
};

int main() {
  B b;
  return 0;
}

Nicer solution:

class B : public A {
public:
  B() : B(filter()) {}

private:
  B(std::tuple<int, std::map<int, int>> t)
    : A(std::get<0>(t)), m_aStuff(std::move(std::get<1>(t))) {}

  static std::tuple<int, std::map<int, int>> filter() {
    std::map<int, int> aStuff;
    //new(&m_aStuff)std::map<int,int>;
    return make_tuple(0, aStuff);
  }    

  std::map<int, int> m_aStuff;
};
Yuki
  • 3,857
  • 5
  • 25
  • 43
  • Thanks, seems like this should work. I'll indeed search for another solution but I was invested in the syntactic sugar. – turoni Jun 06 '17 at 17:22
  • Thanks that indeed seems pretty nice. – turoni Jun 06 '17 at 19:51
  • If I ever started a voting ring it would be to upvote this answer again. It integrates perfectly in my code and looks nice. – turoni Jun 07 '17 at 07:57
1

As your unasked question "How do I fix it?" was answered by Yuki without any details I will provide an answer "What happend there?".

In C++ object is constructed from base class. So the order is following:

A::A(int); // base class constructor
B::B(); // inherited class constructor

All member objects are initialized in initializer list:

B::B()
: // Initializer list begin.
A(), // Base class initialized first.
m_aStuff() // Members of current class are initialized next.
{ // End of initializer list.
    // All members are safe to use.
}

This is the order of object initialization. As you can see here the member m_aStuff initialized after base constructor A::A(int). Constructor is a member function and as every function requires arguments to be evaluated. So the function int B::filter() is called before object is initialized. Which means that member variables are not initialized either. In other words the order of execution is:

  1. Call B::filter()
  2. Modify member variable B::m_aStuff
  3. Call base class constructor A::A(int)
  4. Initialize member variable B::m_aStuff

Obviously step 2 is modifying variable before it was initialized. Depending on implementation of std::map this can cause different behavior (probably undefined).

In fact following two constructions are equal:

B::B() :
A(0)
{}

and

B::B() :
A(0),
m_aStuff()
{}

but in the second case you are initializing it explicitly, while in first case compiler will generate this code for you.

Your solution new(&m_aStuff)std::map<int,int>; initializes object before using it which makes behavior more defined. But next the generated constructor of class B will kick in and B::m_aStuff will be initialized once again. This will set your map to initial state (though I can imagine scenarios in which memory will leak).

Teivaz
  • 5,462
  • 4
  • 37
  • 75
  • Thanks for writing it out, that's how I thought it went. I indeed kinda deleted my question in my last edit but I don't think it matters anymore. I guess my question should be, how do I skip step 4? – turoni Jun 06 '17 at 19:48
  • @turoni you can not and should not skip step 4. This is standard and expected behavior. – Teivaz Jun 07 '17 at 09:13
  • I get it would be really bad and dangerous but I would have liked if c++ allowed it. Sort of "know that you should never do this but we still give you the option if you want too" – turoni Jun 07 '17 at 09:39