Starting from C++17, in certain circumstances, it may appear that the constructor is inherited.
The following is legal:
struct A { A(int x): x(x) {} int x; }; struct B: public A{ }; int main(){ B b {1}; }
Even though B does not have using A::A;, you can construct B b {1}; without any issue.
The reason is not automatic constructor inheritance. Look at the following:
struct A { A(int x, int y): x(x), y(y) {} int x, y; }; struct B: public A{ }; int main(){ //B b {1, 2}; // error, would have worked with `using A::A;` B b {{1, 2}}; // works, would error with `using A::A;` }
The reason is, starting from C++17, struct B above is considered an aggregate. So it behaves similar to the following:
struct A { A(int x): x(x) {} int x; }; struct B { A base; }; int main(){ B b {1}; // also valid in C++14, since struct B above is aggregate either way }
Starting from C++20, you can even use parentheses:
#include <vector> struct A { A(int x): x(x) {} int x; }; struct B: public A{ }; int main(){ B b (1); // illegal in C++17, legal in C++20 B d (int()); // most vexing parse! I recommend using braces, although in emplace_back() you have no choice B e {int()}; // int() = 0, fine std::vector<B> v; v.emplace_back(1); // illegal in C++17, legal in C++20 }
Credit: Automatic constructor inheritance in C++20 and linked questions.