13

The Clang documentation neatly explains that

If a class or struct has no user-defined default constructor, C++ doesn't allow you to default construct a const instance of it like this ([dcl.init], p9)

The following code has such a user-defined default constructor for Base, but g++ and Clang disagree whether the default constructor for Derived is user-defined, even though Derived does explicitly inherit all of the Base constructors (using the new C++11 inheriting constructors feature)

#include <iostream>

class Base
{
public:
    Base(): b_(0) {}  // look! user-defined default constructor
    void print() const { std::cout << b_ << "\n"; }
private:
    int b_;
};

class Derived
:
    public Base
{
    using Base::Base; // does this constitute a user-defined default constructor?
};

int main()
{
    Base const b;
    b.print();    // 0 for g++ & CLang

    Derived const d;
    d.print();    // 0 for g++, Clang: "default initialization of an object of const type 'const Derived' requires a user-provided default constructor"
}

g++ 4.8 happily accepts this code, but Clang 3.3 does not. What does the Standard say?

NOTE: without a user-defined default constructor for Base, neither g++ 4.8 nor Clang 3.3 accept Base const b; (whereas e.g. g++ 4.7.2 previously accepted that). Given that g++ knows about the rule, I would think that this implies that g++ regards the default constructor for Derived as user-defined. But Clang 3.3 thinks otherwise.

UPDATE: based on @JesseGood 's answer that 0/1 argument constructors are never inherited, I tried changing the Base constructor to

Base(int b = 0, void* = nullptr): b_(b) {}

but it does not resolve the Clang error.

Community
  • 1
  • 1
TemplateRex
  • 69,038
  • 19
  • 164
  • 304

1 Answers1

7

Clang is correct.

The relevant passage about const instances is from 8.5p7:

If a program calls for the default initialization of an object of a const-qualified type T, T shall be a class type with a user-provided default constructor.

Since Base(): b_(0) {} is user-provided, Base const b; is fine.

The next important part is 12.9p3:

For each non-template constructor in the candidate set of inherited constructors other than a constructor having no parameters or a copy/move constructor having a single parameter, a constructor is implicitly declared with the same constructor characteristics unless there is a user-declared constructor with the same signature in the class where the using-declaration appears

The important part here is the bolded text. I believe this rules out your case since Base() is a constructor having no parameters. This means that the Derived does not have a user provided default constructor (although one still is implicitly declared).

What this also means is that default, copy and move constructors from a base class are never inherited.

Jesse Good
  • 50,901
  • 14
  • 124
  • 166
  • +1 thanks for your answer. If I change the `Base` constructor signature to `Base(int b = 0, void* = nullptr): b_(b) {}`, the default-constructor is suppressed. But Clang still gives the error on `Derived` (and g++ accepts it). Does a constructor with 2 defaulted parameters also count as a non-inherited constructor with no parameters? – TemplateRex May 28 '13 at 06:01
  • 2
    @rhalbersma: Yes, it still counts as a non-inherited ctor with no parameters. `Base(int)` and `Base(int, void*)` are inherited though. (but, the default arguments are not inherited). – Jesse Good May 28 '13 at 08:39
  • If I understand the 12.9p3 part correctly, the correct interpretation (for C++11) would be to ignore `using Base::Base` entirely, since the only constructor that could be inherited has no parameters, so it isn't. So it wouldn't work, even without the const qualification. Curiously, the text standard nothing about why default construction are discriminated against in the heritage. Also, it appears this clause has been removed since: class inheriting constructors and also declaring one (so no implicit default) fails to compile no-argument declaration with g++-4.8, but works with g++-7.3 – Marc van Leeuwen Oct 23 '18 at 17:12