I was going through a book called Programming Principles and Practices using C++ but found a strange behavior of class construction.
Suppose I have a class as follows:
class Foo {
public:
Foo(int x)
: y { x } { }
private:
int y;
};
and I have another class which has an instance of class Foo as its member object
class Bar {
public:
Bar(Foo x)
: y { x } { }
private:
Foo y;
};
When I do the following:
int main()
{
Bar obj_1 { Foo { 1 } };
Bar obj_2 { 2021 }; // this doesn't give me error?
return 0;
}
obj_1 was constructed as specified in the constructor, but obj_2 doesn't give me any error message and to me it seems it just magically works.
My intention of having a member of a class as an instance of another class was to force the constructor to take a class instance as its argument, but not an integer.
Why doesn't it give me incorrect type error?
You can prevent this implicit conversion by declaring the Foo constructor explicit
explicit Foo(int x) : y { x } { }
in main this would require the caller to change their obj_2 instantiation to
Bar obj_2 { Foo{2021} };
Related
I was somehow surprised that the following code compiles and runs (vc2012 & gcc4.7.2)
class Foo {
struct Bar { int i; };
public:
Bar Baz() { return Bar(); }
};
int main() {
Foo f;
// Foo::Bar b = f.Baz(); // error
auto b = f.Baz(); // ok
std::cout << b.i;
}
Is it correct that this code compiles fine? And why is it correct? Why can I use auto on a private type, while I can't use its name (as expected)?
The rules for auto are, for the most part, the same as for template type deduction. The example posted works for the same reason you can pass objects of private types to template functions:
template <typename T>
void fun(T t) {}
int main() {
Foo f;
fun(f.Baz()); // ok
}
And why can we pass objects of private types to template functions, you ask? Because only the name of the type is inaccessible. The type itself is still usable, which is why you can return it to client code at all.
Access control is applied to names. Compare to this example from the standard:
class A {
class B { };
public:
typedef B BB;
};
void f() {
A::BB x; // OK, typedef name A::BB is public
A::B y; // access error, A::B is private
}
This question has already been answered very well by both chill and R. Martinho Fernandes.
I just couldn't pass up the opportunity to answer a question with a Harry Potter analogy:
class Wizard
{
private:
class LordVoldemort
{
void avada_kedavra()
{
// scary stuff
}
};
public:
using HeWhoMustNotBeNamed = LordVoldemort;
friend class Harry;
};
class Harry : Wizard
{
public:
Wizard::LordVoldemort;
};
int main()
{
Wizard::HeWhoMustNotBeNamed tom; // OK
// Wizard::LordVoldemort not_allowed; // Not OK
Harry::LordVoldemort im_not_scared; // OK
return 0;
}
https://ideone.com/I5q7gw
Thanks to Quentin for reminding me of the Harry loophole.
To add to the other (good) answers, here's an example from C++98 that illustrates that the issue really doesn't have to do with auto at all
class Foo {
struct Bar { int i; };
public:
Bar Baz() { return Bar(); }
void Qaz(Bar) {}
};
int main() {
Foo f;
f.Qaz(f.Baz()); // Ok
// Foo::Bar x = f.Baz();
// f.Qaz(x);
// Error: error: ‘struct Foo::Bar’ is private
}
Using the private type isn't prohibited, it was only naming the type. Creating an unnamed temporary of that type is okay, for instance, in all versions of C++.
For anyone else coming here and needs a workaround (e.g. for declaring a function that accepts the private type), this is what I did:
void Func(decltype(Foo().Baz()) param) {...}
I have the following code:
#include <iostream>
class First {
public:
Second* x;
void make_value(Second* sec_);
First() {
// Initialization
}
};
class Second {
public:
First* y;
Second() {
// Initialization
}
};
void First::make_value(Second* sec_) {
x = sec_;
}
void main() {
fir = new First();
sec = new Second();
fir->make_value(sec);
}
The two classes each have a member variable of the other class, which does not work for obvious reasons.
My question is whether or not there is a way to late-initialize variable x after class Second has been initialized. If not, what alternatives are there?
For any uses where the compiler doesn't need the definition of a class, a forward declaration will suffice. Pointers and references to types do not require a definition.
class Second; // forward declare
class First {
public:
Second* x;
void make_value(Second* sec_);
First() {
// Initialization
}
};
class Second {
public:
First* y;
Second() {
// Initialization
}
};
void First::make_value(Second* sec_) {
x = sec_;
}
class foo{
public:
bar steal_the_moveable_object();
private:
bar moveable_object;
};
main(){
foo f;
auto moved_object= f.steal_the_moveable_object();
}
How can implement steal_the_movebale_object to move the moveable_object into the moved_object ?
You can simply move the member directly in the return statement :
class foo
{
public:
bar steal_the_moveable_object()
{
return std::move(moveable_object);
}
private:
bar moveable_object;
};
Beware that this may not be a good idea though. Consider using the following instead so that the method can only called on R-Values :
class foo
{
public:
bar steal_the_moveable_object() && // add '&&' here
{
return std::move(moveable_object);
}
private:
bar moveable_object;
};
int main()
{
foo f;
//auto x = f.steal_the_moveable_object(); // Compiler error
auto y = std::move(f).steal_the_moveable_object();
return 0;
}
my question is as follows: Suppose I have:
class Foo
{
public:
Foo() {}
void setInt(int i) { myInt = i; }
int getInt() { return myInt; }
private:
int myInt;
};
class Bar
{
public:
Bar(Foo f) { /* do something with f.getInt() */ }
};
Now I have another class that has Bar as a member vairable:
class BarUser
{
public:
BarUser();
private:
Bar bar;
};
I want to write BarUser's constructor, however I want to initialize Bar with a Foo member that has 3 as its integer. I.e.:
Foo f;
f.setInt(3);
Bar b(f);
However since I have Bar as a class member, I cannot write all this code in the initialization list... What I mean is:
BarUser::BarUser() : bar(/* Foo after executing f.setInt(3) */)
{ ... }
Suppose assignment operator is not allowed for Bar - how can I initialize it as intended?
Thanks!
If you can't change Foo, write a function:
Foo make_foo(int i) {
Foo f;
f.setInt(i);
return f;
}
then initialize with bar(make_foo(3)).
You've sort of shot yourself in the foot by giving Foo a constructor but no int constructor. You might be better off adding an explicit constructor to Foo that takes an int.
Here is my code:
class Foo
{
public:
Foo(const char*);
};
class Bar
{
public:
Foo bu("adfds");
};
int main()
{
return 0;
}
Foo::Foo(const char* iLoc)
{ }
When I try to create a Foo class within the Bar class, I get the syntax error when trying to pass to the constructor. Why is this the case?
I prefer this to egrunin's answer as you don't have to track memory allocation.
class Bar
{
private:
Foo bu;
public:
Bar()
: bu("adfds")
{
}
};
You can't initialize bu in the class declaration. Is this what you want?
class Bar
{
public:
Foo *bu;
Bar() {
bu = new Foo("adfds");
}
};
Edit
As pointed out in the comments, here's a way of doing it without making bu a pointer:
class Bar : bu("adfds")
{
public:
Foo bu;
};