I got a struct B that is derived from a struct A.
struct A{
int a;
};
struct B : public A{
int b;
};
Is there a straight way to initialize an object of type B without providing a constructor, let's say using initializer-list?
Some more insight:
I got two structs that I use to pass data between threads; the second one holds the same data as the first one, with the addition of some synchronization variable. I could make the first struct a data member of the second one, or just duplicate the declaration of data members in the second struct, to easily use the initializer-list; but I think that in this particular application it would be logically more correct that the second struct extends the first one.
There isn't an extremely concise solution, but there is a solution at least:
#include <type_traits>
#include <utility>
struct B : A
{
int b;
template <typename ...Args,
typename = typename std::enable_if<
std::is_constructible<A, Args&&...>::value>
B(int x, Args &&... args)
: b(x), A(std::forward<Args>(args)...)
{ }
};
This solution isn't exactly short, but it is general. The new constructor for B exists only in those specializations which make sense, thanks to the enable-if SFINAE, so B is precisely as constructible as it can be.
There is one more danger here that I didn't address, namely how explicit the new constructor should be. Ideally it should be as explicit as the matching A constructor, but that's a bit hard to detect programatically (as done in N4064 for pairs and tuples).
You can't use aggregate initialization for B, because it's not an aggregate, as per [dcl.init.aggr]/1:
An aggregate is an array or a class (Clause 9) with no user-provided constructors (12.1), no private or
protected non-static data members (Clause 11), no base classes (Clause 10), and no virtual functions (10.3).
Update: Kerrek SB provided a good solution using template constructor, but if you like, you can add quite straightforward non-template constructor for B:
struct B : public A{
int b;
B(const A& a_, int b_) : A(a_), b(b_) {}
};
And use it with one extra pair of braces:
B b {{3}, 5};
Related
As we know, in C++20 a class that has a user-declared constructor is not aggregate.
Now, consider the following code:
#include <type_traits>
template <bool EnableCtor>
struct test {
template <bool Enable = EnableCtor, class = std::enable_if_t<Enable>>
test() {}
int a;
};
int main() {
test<false> t {.a = 0};
}
Neither GCC nor CLang compile this code. So, the class is not an aggregate, despite the fact that there is no instantiated constructor here.
Is the constructor template considered to be a "declared constructor"? What does the Standard say about such a situation?
The definition of aggregate has changed a lot, but the relevant part here has been pretty constant. The C++20 wording in [dcl.init.aggr] says:
An aggregate is an array or a class ([class]) with
no user-declared or inherited constructors ([class.ctor]),
no private or protected direct non-static data members ([class.access]),
no virtual functions ([class.virtual]), and
no virtual, private, or protected base classes ([class.mi]).
Note that it just says no declared constructors, period. Not something subtle about whether or not the constructor is viable for a particular specialization of a class template. Just none at all.
So given something like this:
template <bool B>
struct X {
int i;
X(int i) requires B;
};
X<false> still isn't an aggregate, even though its only declared constructor isn't viable for that specialization. Doesn't matter. Aggregate-ness is a property of declarations only.
As the title says, I'm wondering if there's a way to expose a protected constructor in a derived class (that is, to change access from protected to public). Consider the following (contrived) example:
struct A {
int a;
int b;
protected:
A(int, int) {};
void do_something();
};
struct B : A {
B() : A(0, 0) {};
using A::A;
using A::do_something;
};
int main() {
B b(0, 0); // error: 'A::A(int, int)' is protected
//B b{}; // no errors
b.do_something();
}
So one can change the access of protected member functions, but not of constructors? If so, what is the rationale for the restriction?
Workaround: Variadic template with argument forwarding can serve as a workaround that would perform identically.
Speculation for the rationale: by default the constructor of base classes are not "inherited" in the regular sense; the constructor of derived class must construct the base class instance(s) first. When using is used upon regular member functions, it introduces the functions into the current declarative region thus changing access; on constructors, using only brings base constructor to the "normal inheritance" level (identical to other member functions without using). Because only in some cases is constructor inheritance (reuse of base constructors) useful, the standard committee people designated using X::X syntax for constructor inheritance rather than the stronger namespace teleporting.
What you are trying to do is to call the constructor B, with two parameters, but it doesn't take any.
The error you get, is about the constructor of B, being protected, but if you look carefully, that is not the constructor you declared:
error: 'B::B(int, int)' is protected
Why is that?
Because the compiler is trying to resolve your call to something valid, and it finds the base class constructor to match the signature. But it is still protected so it fails.
What you need to do, to "relaunch" the protected constructor to an inherited class is to provide a matching constructor:
struct B : A {
B() : A(0, 0) {};
B(int a, int b) : A(a, b) {};
using A::A;
using A::do_something;
};
int main() {
B b;
B b2(1, 2);
//b.do_something();
}
http://cpp.sh/9tvik
The reason why, you cannot change the accessor level of a constructor the same way you change it for a method, is most probably to be searched into the non-virtual nature of the constructors. [citation needed :)]
I wanted to pass-in functions as arguments to a templated function without having any indirection. To achieve this I created two nested structs, each defining the function I wish to pass in to the templated function. Each of the structs accesses data members from the outside class B:
namespace A{
class B{
public:
B();
template <typename T>
void templatedFunction(T t){
//I pass one of the struct objects in to here, to invoke the desired function
t();
}
private:
struct X{
void operator(){
do();
}
void do(){
//Accesses the data members of class B
e->doSomething();
}
};
struct Y{
void operator(){
do();
}
void do(){
//Accesses the data members of class B
d.doSomething();
}
};
C* c;
D d;
E* e;
};
}
and the compiler errors I get are pretty much all of the format:
error: invalid use of non-static data member B::d
for the lines within the struct accessing the class data members and on the lines declaring the data members in B.
A nested class in C++ does not (automatically) have access to an instance of the containing class. It's just a class definition like any other. You need a B instance to access non-static data members of B.
You can restructure your nested class Y as
struct Y
{
void operator()( B& b ){
do( b );
}
void do( B& b ){
//Accesses the data members of class B
b.d.doSomething();
}
};
and fix your function template and calls, and class X, accordingly.
Note that your presented code for operator(), with no argument list, would not have compiled, but I'm not downvoting since you are stopped by another compilation error (i.e., possibly it is the real code you're showing).
If you have a struct (or class for that matter) nested in another class, it it not treated specially in any way. It works exactly the same as if the struct was defined outside of the enclosing class. The only thing which is different is the scope of nested class name. So, if you have
class A { class B{}; };
and
class B{}; class A {};
the only difference is that outside class A you need to name the class B as A::B in the first case, and just B in the second class. There is no other difference, and in particular class B does not gain any special access to class A members.
This question already has answers here:
Inheriting constructors
(8 answers)
Closed 8 years ago.
I just want to check this:
If I have a derived class that has no new members. In case that the base class has already a long constructor (many parameters), the creation of the constructor in the derived class that just throws all the parameters to the base constructor is somewhat a boring (and obviously not necessary) process. Is there a way to tell the compiler to do it automatically? I suppose not, but just needed to check. I haven't found anything on this topic but still sorry for a duplicate if this is the case...
//I'm not interested in IDE automatic generation features (although I would welcome an advice of that type either), just a compiler...
You could use constructor inheritance (available since C++11)
In the definition of the derived class write:
public:
using base_class::base_class;
where base_class is your base class.
It really depends on the version of C++ you are targetting.
The first answer is that in C++11 it is easy:
you can either use inheriting constructors
or perfect forwarding
Implementation:
// Inheriting
struct D: B { using B::B; };
// Forwarding
struct D: B {
template <typename... T>
D(int a, T&&... args): B(std::forward<T>(args)...), _a(a) {}
int _a;
};
As demonstrated, while more verbose perfect forward is also more versatile.
If you are stuck in C++03, though, all is not lost: just refactor the base constructor so it takes less parameters (ideally one):
// Before
struct B { B(int a, int b, int c, ...); };
// After
struct Pack { Pack(int a, int b, int c, ...); };
struct B { explicit B(Pack p); };
Easy Peasy.
If you are initializing several parameters in the base constructor and their values are being passed to the constructor, you can overcome this problem without C++11 by implementing setX() functions instead of initializing the values in the constructor.
BaseClass::BaseClass(int arg1, int arg2, int arg3, ...) :
_member1(arg1),
_member2(arg2),
_member3(arg3),
...
{}
Change it to:
BaseClass::BaseClass() :
_member1(0),
_member2(0),
_member3(0),
...
{}
void BaseClass::setMember1(int value) { _member1 = value; }
C++11, ยง9/7:
A standard-layout class is a class that:
has no non-static data members of type non-standard-layout class (or array of such types) or reference,
has no virtual functions and no virtual base classes,
has the same access control for all non-static data members,
has no non-standard-layout base classes,
either has no non-static data members in the most derived class and at most one base class with non-static data members, or has no base classes with non-static data members, and
has no base classes of the same type as the first non-static data member.
So, is there a way to make a class with standard layout non-copyable? If yes, how?
Inheriting privately from boost::noncopyable will not work, because it made copy constructor private (hence not a standard layout). The boost::noncopyable's implementation is like this :
class noncopyable
{
protected:
noncopyable() {}
~noncopyable() {}
private: // emphasize the following members are private
noncopyable( const noncopyable& );
const noncopyable& operator=( const noncopyable& );
};
Because of the private section, it is not a standard layout class. I am also note sure if private inheritance break any standard layout rule.
#include <boost/noncopyable.hpp>
#include <iostream>
const int N = 50;
struct A
{
int data[N];
};
struct B : private boost::noncopyable
{
int data[N];
};
struct C
{
A data[10];
};
struct D : private boost::noncopyable
{
B data[10];
};
int main() {
std::cout<<sizeof(A)<<std::endl;
std::cout<<sizeof(B)<<std::endl;
std::cout<<sizeof(C)<<std::endl;
std::cout<<sizeof(D)<<std::endl;
}
The output is :
200
200
2000
2004
The example above shows that inheriting privately from boost::noncopyable changes the class into NOT standard layout compliant.
I am not sure if this is a g++ bug (I am using g++ 4.6.1), or the standard is somehow violated.
I think there is a confusion here:
the standard layout property is affected by attributes (and only attributes)
the copyable property is affected by methods (their presence, absence and accessibility)
The two concepts are orthogonal.
UPDATE:
The following display the very same behavior that boost::noncopyable:
#include <iostream>
struct foo {};
struct B : foo { int data; };
struct D : foo { B data; };
int main() {
D d;
std::cout << (char*)(&d.data) - (char*)(&d) << "\n";
}
The result is 4.
I believe this is because of:
has no base classes of the same type as the first non-static data member.
Indeed, experiment shows that introducing a int a; in D prior to data does not increases its size. I think that the fact that B inherits from foo means that data (first non-static data member) is considered to be the same type as foo (base class of D).
This leads to an ambiguity: foo* f = &d would have the same address as foo* g = &b.data; if the compiler did not introduce this padding.
There are two things you need to do to make your class non copyable:
Make the copy constructor private.
Make the assignment operator private. (Where it gets assigned another type of the same kinds as the class itself).
You don't need to inherit from some boost class just to get that behavior.
And may I add, who cares about some fancy 'standard layout' idea. Program what you need and don't succumb to this extreme space theory.