Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

All versions of GCC struggle with default member initializer, that captures this, combined with inherited constructors

This story is similar to my previous question. All versions of GCC that support C++11, have this exact behaviour. I could not find any other compiler that struggles with my test case.

The test case:

struct BaseFooWrapper
{
    BaseFooWrapper(int qux)
    { }
};

struct Foo
{
    Foo(BaseFooWrapper & foo)
        : foo(foo)
    { }

    BaseFooWrapper & foo;
};

struct SomeFooWrapper : public BaseFooWrapper
{
    using BaseFooWrapper::BaseFooWrapper;


    Foo foo{*this};
};

int main()
{
    SomeFooWrapper wrapped_foo(1);
    return 0;
}

Live on godbolt.com


This piece of code compiles with clang (3.4 through 4.0), icc (16, 17), Visual C++ (19.00.23506).

If I replace constructor inheritance with a hand-written version, then GCC starts to compile the code:

struct BaseFooWrapper
{
    BaseFooWrapper(int qux)
    { }
};

struct Foo
{
    Foo(BaseFooWrapper & foo)
        : foo(foo)
    { }

    BaseFooWrapper & foo;
};

struct SomeFooWrapper : public BaseFooWrapper
{
    SomeFooWrapper(int qux)
        : BaseFooWrapper(qux)
    { }


    Foo foo{*this};
};

int main()
{
    SomeFooWrapper wrapped_foo(1);
    return 0;
}

Live on godbolt.com


Obviously it is not very handy, especially when you have many such classes, and leads to boilerplate code. Basically, the very thing that inheriting constructors are designed to fix. This behaviour of GCC renders this great c++11 feature unavailable in such cases.

So I am really curious whether I am doing something illegal with regard to the standard or this is a bug in GCC?


Edit:

Filed a bug report.

like image 694
GreenScape Avatar asked May 19 '17 05:05

GreenScape


2 Answers

The problem is not the constructor inheritance, but this line:

Foo foo{*this};

GCC appears to think it will need a default constructor for struct Foo as well, and since the class has a reference, it is not able to do so.

error: no matching function for call to 'Foo::Foo()'
<source>:14:5: note: candidate: Foo::Foo(BaseFooWrapper&)
     Foo(BaseFooWrapper & foo): foo(foo)
     ^~~
<source>:14:5: note:   candidate expects 1 argument, 0 provided
<source>:10:7: note: candidate: constexpr Foo::Foo(const Foo&)

By adding the default constructor it appears to think it needs, then the code compiles:

struct Foo
{

    Foo():foo(*new BaseFooWrapper(0))
    {

    }
    Foo(BaseFooWrapper & foo): foo(foo)
    { 

    }

    BaseFooWrapper & foo;
};

It does look like a bug.

like image 115
didiz Avatar answered Nov 15 '22 10:11

didiz


I could be wrong, but quoting from the n4527 draft standard in [class.this]:

In the body of a non-static (9.3) member function, the keyword this is a prvalue expression whose value is the address of the object for which the function is called. The type of this in a member function of a class X is X*.

In the OP's code, the initialisation of SomeFooWrapper::foo is not taking place in a member function. Therefore the keyword this has no reasonable meaning as far as the standard is concerned.

Or have I missed something?

like image 2
Richard Hodges Avatar answered Nov 15 '22 08:11

Richard Hodges