I have something like this:
class Bar;
class Foo()
{
public:
Foo() : bar(new Bar());
Bar& GetBar() { return *bar.get(); }
private:
std::unique_ptr<Bar> bar;
};
void main()
{
Foo foo;
auto bar1 = foo.GetBar();
auto bar2 = foo.GetBar(); //address of bar2 != address of bar1. why?
Bar& bar3 = foo.GetBar();
Bar& bar4 = foo.GetBar(); //address of bar3 == address of bar4.
}
It seems the 'auto' variables are copies as I don't get Bars back with the same memory address.
If I explicitly define the variables as Bar references (Bar&) then everything works as I would expect.
I should mention I'm compiling in vs2012. What's going on here?
Thanks.
auto bar1 = … always declares a copy. auto &&bar1 selects the closest possible reference type, which is what you want.
auto && is the perfect forwarding idiom.
You can also use other compound types with auto, such as auto const & or auto * if you want to be specific.
auto works like template argument deduction. bar1 and bar2 have types Bar, so they are independent copies; bar3 and bar4 have type Bar & and are references to the same *foo.bar.
Code:
X& getter() {
static X value;
return value;
}
print("X:");
X x0 = getter();
auto x0a = getter();
x0.printAddress();
x0a.printAddress();
print("X&:");
X& x1 = getter();
auto& x1a = getter();
x1.printAddress();
x1a.printAddress();
print("const X&:");
const X& x2 = getter();
const auto& x2a = getter();
x2.printAddress();
x2a.printAddress();
print("X&&:");
print("Rvalue can't be bound to lvalue");
X&& x3 = getter();
auto&& x3a = getter();
x3.printAddress();
x3a.printAddress();
Result:
X:
0037F807
0037F7FB
X&:
00D595BA
00D595BA
const X&:
00D595BA
00D595BA
X&&:
Rvalue can't be bound to lvalue
00D595BA
Conclusion:
auto means: "replace me with type, unless I am auto&& then find the most suitable form".
Related
If I don't define a constructor in a struct, I can initialize it by just picking a certain value like this:
struct Foo {
int x, y;
};
Foo foo = {.y = 1};
But if I add new default constructor then I lose this feature:
struct Bar {
int x, y;
Bar(int value) : x(value), y(value) {}
};
Bar bar1 = 1;
Bar bar2 = {.y = 2}; // error: a designator cannot be used with a non-aggregate type "Bar"
Is it possible to have both ways?
I tried adding the default constructor Bar () {} but it seems to not work either.
You can't have your cake and eat it too. If the object has a constructor it is no longer an aggregate, and only aggregates can be initialized with designated initializers. You can't use constructors for arbitrary initialization logic with aggregates.
Are we toasted though? No, because there's the "named constructor" idiom. It's essentially just a static member function that returns an initialized object, and able to perform some logic. The idiom is compatible with aggregate initialization.
struct Foo {
int x, y;
static Foo filled_with(int value) {
return {.x = value, .y = value};
}
};
Foo foo = {.y = 1}; // Still an aggregate.
Foo foo2 = Foo::filled_with(2); // Custom logic
There's not even any copying or moving with this approach, because C++17 removed the possibility for those. foo2 is initialized directly with whatever the static member does.
Similar to what ellipticaldoor wrote:
struct FooBase {
int x = 0, y = 0;
};
struct Foo : FooBase {
Foo(int x_) : FooBase{.x = x_} { }
Foo(FooBase &&t) : FooBase{t} {}
};
Foo foo = {{.y = 1}};
Foo foo2{1};
So far this is the closest thing I can find:
struct Vec2 {
int x, y;
};
struct Bar {
int x, y;
Bar(int value) : x(value), y(value) {}
Bar(Vec2 value) : x(value.x), y(value.y){};
};
Bar bar1 = 1;
Bar bar2 = {{.y = 2}};
But you need to use double params
You can use a data member initializer instead so the type remains an aggregate:
struct Foo {
int x = 0, y = x;
};
Foo foo1 = {.y = 6}; // Foo{0, 6}
Foo foo2{7}; // Foo{7, 7}
(Though it can't be implicitly constructed from int)
While looking into some code, I came across a construct with the following line:
if (const auto& foo = std::get_if<MyType>(&bar)) // note the ampersand!
where bar is a std::variant<MyType, OtherType>. The problem here is that get_if may return a null pointer and I don't understand why the statement works.
Consider this similar MCVE:
#include <iostream>
struct Foo { int a = 42; };
Foo* f() { return nullptr; }
int main() {
const auto& foo = f(); // Returns a nullptr that binds to Foo*& - UB?
//static_assert(std::is_same<decltype(foo), const Foo*&>::value); // -> Fails
//const Foo*& bar = f(); // -> Fails
if (foo) std::cout << foo->a << std::endl;
else std::cout << "nullpointer" << std::endl;
}
The first line of main() works fine, and I would expect the type of barto be const Foo*&, but the static assertion fails. Unsurprisingly, the following line also fails to compile with cannot bind non-const lvalue reference of type 'const Foo*&' to an rvalue of type 'const Foo*'.
What happens in the first statement of main? Is this UB or does the standard contain some hidden secret that allows this to be legal? What is the type of bar?
Note that for const auto& foo, const is qualified on the auto part, i.e. the pointer but not the pointee. Then the type of foo would be Foo* const &, which is a reference to const (pointer to non-const Foo), but not const Foo* &, which is a reference to non-const (pointer to const Foo).
And the lvalue-reference to const could bind to rvalue returned by f(), so const auto& foo = f(); works fine; const Foo*& bar = f(); won't work because bar is an lvalue-reference to non-const; which can't bind to rvalue. Changing the type of bar to const Foo * const & or Foo* const & (same as foo) would make it work.
Consider the following class hierarchy, made from aggregates:
struct Foo {
int k;
double d;
};
struct Bar : Foo {
int i;
};
Now, let's say I would like to initialize an object of type Bar from an object of type Foo, giving extra argument for i. (For reason to dull to discuss here, adding a constructor to Bar which accept Foo and int, or modifying definition of Bar or Foo in any other way is out of question). In C++17 I would use aggregate initialization:
auto make(const Foo& f) {
return Bar{f, 42};
}
This is not available in C++14. Is there anything else I can do mimic desired behavior in C++14? I am trying to avoid something like
auto make(const Foo& f) {
Bar b;
b.k = f.k;
b.d = f.d;
b.i = 42;
return b; // or return Bar{f.k, f.d, 42};
}
Where the thing I am trying to avoid is making make aware of internals of Foo - i.e. make is fine to know how to initialize extra members of Bar, but would prefer not to initialize members of Bar which are common with Foo.
auto make(const Foo& f) {
Bar b;
static_cast<Foo&>(b) = f;
b.i = 42;
return b;
}
I see some code like:
Foo const& foo = val; // (A)
const Foo& foo = val; // (B)
Just different placement of const.
Though both will work fine, I would just want to know the proper grammar.
I read it as follows from R->L.
(A) foo is a const reference to type Foo - not sure if val is const
(B) foo is a reference to const type Foo - meaning val is const
Given that references are not objects, and technically all references are const (they can't refer to another object),
Grammar perspective, is (A) Foo const& foo = val; considered a "confusing" definition?
And just use the following instead:
const Foo& foo = val; // referring to const Foo
Foo& foo = val; // referring to non-const Foo
const applies to the thing on its left, unless nothing is there, then it applies to the thing on its right instead.
These two statements are identical:
Foo const& foo = val; // (A)
const Foo& foo = val; // (B)
They both mean the same thing:
foo is a non-const (1) reference to a const Foo object, and is being initialized as a reference to an object named val.
(1): by definition, a reference can't be changed once initialized, so const vs non-const doesn't really apply to a reference itself, as it is implicitly const. But const does apply to the thing that is being referenced.
Foo const& foo = val; // (A)
const Foo& foo = val; // (B)
Both are identical definitions and are just a matter of style:
reference to const object.
it would be different for:
const Foo* fooC = nullptr; // (C)
Foo* const fooD = nullptr; // (D)
You cannot reassign fooD but you can reassign fooC.
Object pointed by fooC cannot be modified through fooC.
Object pointed by fooD can be modified through fooD.
They're identical grammatically. And
I read it as follows from R->L.
(A) foo is a const reference to type Foo
I think you didn't read it correctly. It should be something like
Foo const &
Foo/const/reference to
then you should get "reference to const Foo`. So when reading from R->L, (A) is more clear.
I am working on a matrix view class, of which constructor takes a matrix as a parameter and binds it to a const reference member. I would very much like to avoid binding rvalues, since they don't bind via a constructor parameter, and we end up with a dangling reference. I came up with the following (simplified code):
struct Foo{};
class X
{
const Foo& _foo;
public:
X(const Foo&&) = delete; // prevents rvalue binding
X(const Foo& foo): _foo(foo){} // lvalue is OK
};
Foo get_Foo()
{
return {};
}
const Foo get_const_Foo()
{
return {};
}
Foo& get_lvalue_Foo()
{
static Foo foo;
return foo;
}
int main()
{
// X x1{get_Foo()}; // does not compile, use of deleted function
// X x2{get_const_Foo()}; // does not compile, use of deleted function
X x3{get_lvalue_Foo()}; // this should be OK
}
Basically I delete the constructor that takes const Foo&& as a parameter. Note that I need the const since otherwise someone may return const Foo from a function, and in that case it will bind to the const Foo& constructor.
Question:
Is this the correct paradigm of disable rvalue binding? Am I missing something?