0

Background: I had something like (but with significant code in Z that I didn't want to change and the warning is enabled globally - I know I could disable it):

#pragma warning (error: 4355)
struct A;
struct B;
struct MapCompare {
   A*the;
   MapCompare(A*a):the(a) {;}
   bool operator()(int,int) const;
};
class Z {
  A a;
  B b;
  std::map<int,int,MapCompare> m;
public:
  Z():m{MapCompare{&a}} {;}
  ...
};

But I want to change to use both a and b inside MapCompare. The less impactful variant seemed to be:

#pragma warning (error: 4355)
struct A;
struct B;
struct AB {
   A a;
   B b;
};
struct MapCompare {
   AB*the;
   MapCompare(AB*ab):the(ab) {;}
   bool operator()(int,int) const;
};
class Z : public AB {
  std::map<int,int,MapCompare> m;
public:
  Z():m{MapCompare{static_cast<AB*>(this)}} {;}
  ...
};

The error message is:

error C4355: 'this': used in base member initializer list

So Visual Studio 2019 (16.9.5) complains about using this in an initializer, even though I only want the base-part that is initialized (see Order of execution in constructor initialization list ).

Is there some way to tell this to the compiler (and not just disable the warning)? The work-around I found was:

#pragma warning (error: 4355)
struct A;
struct B;
struct AB {
   A a;
   B b;
};
struct MapCompare {
   AB*the;
   MapCompare(AB*ab):the(ab) {;}
   bool operator()(int,int) const;
};
class Z {
  AB ab;
  A&a;
  B&b;
  std::map<int,int,MapCompare> m;
public:
  Z():m{MapCompare{&ab}},a(ab.a),b(ab.b) {;}
  ...
};

Note: I tried to search for similar issues and found Initialize a reference - warning C4355: 'this' : used in base member initializer list but the difference is that I want to extract the base-class part that has been initialized which makes it safe (as long as the members are in this order).

Hans Olsson
  • 11,123
  • 15
  • 38
  • I did not receive 'this' warning when testing the snippet. Could you please check whether certain settings have been changed. – Minxin Yu - MSFT Nov 25 '21 at 02:56
  • @MinxinYu-MSFT I updated - I believe I missed the code enabling that specific message. I also double-checked that I compiled with VS 2019, and added version information. – Hans Olsson Nov 25 '21 at 09:36

1 Answers1

0

Consider this situation which also goes wrong:

class z {
    int a = 5;
    int num;
public:
    z() :num(this->a) {}
};

According to the document Compiler Warning C4355, this pointer cannot be used in the initializer list for a base class. This warning is off by default. What the snippet does is similar to the example provided: CDerived(): CBase(this). This object is not fully constructed and is not safe to use (for the compiler). You need to give it an instance.

Minxin Yu - MSFT
  • 2,234
  • 1
  • 3
  • 14
  • But in this example you can avoid the problem by write `z():num(a) {;}`, since `a` is initialized before `num`. I wanted something similar for using the base-class contents (where the base-class is an aggregate class without any virtual functions). I can understand that this is a too specialized case to implement in the compiler, but I just wanted to check. – Hans Olsson Nov 26 '21 at 13:13