Am I dealing with dangling references?

Aug 16, 2022 at 11:11am
By defining methods of a class &&, and chaining the calls, am I dealing with dangling references??


My class:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
template<typename ConcreteBuilder>
class GenericItemBuilder
{
protected:
	Item item;
public:
	explicit GenericItemBuilder(std::string name) : item{.name = std::move(name)} {}

	Item build() &&
	{
		item.date_added = system_clock::now();
		return std::move(item);
	}
	ConcreteBuilder&& with_description(std::string description) &&
	{
		item.description = description;
		return static_cast<ConcreteBuilder&&>(*this);
	}
	ConcreteBuilder&& marked_as_featured() &&
	{
		item.featured = true;
		return static_cast<ConcreteBuilder&&>(*this);
	}
	ConcreteBuilder&& with_price(float price) &&
	{
		item.price   = price;
		return static_cast<ConcreteBuilder&&>(*this);
	}

};
class ItemBuilder final : public GenericItemBuilder<ItemBuilder>
{
	using GenericItemBuilder<ItemBuilder>::GenericItemBuilder;
};


when called like this:

1
2
3
4
5
void useGenericItemBuilder()
{
	auto directly_loaded_item = ItemBuilder{ "Pot" }.with_description("A decent one").with_price(100).build();

}





Regards


Aug 16, 2022 at 11:42am
Move-constructing an object, from an r-value reference, never leaves you with a "dangling" reference. Instead, even though the move-constructor is allowed to "steal" the resources from the "moved from" object (instead of having to copy them), still the "moved from" object will be left in an undefined but valid state!
Last edited on Aug 16, 2022 at 11:46am
Aug 16, 2022 at 11:51am
Am I introducing temporary objects with this code? Should I remove the && on methods?

Is this code in danger of a dangling reference?
Last edited on Aug 16, 2022 at 11:52am
Aug 16, 2022 at 12:11pm
By using the && ref-qualifier on a member function, you are telling the compiler that this function is supposed to be called on an r-value reference. An r-value reference allows the move-constructor to be called, instead of the copy-constructor. But, as said before, the move-constructor does not produce "dangling" references.

(after move-construction, the "moved from" object is left in a undefined but valid state)

Example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
class Foo
{
public:
    Foo(const int value)
    {
        std::cout << "Constructor" << std::endl;
        data = new int { value };
    }

    Foo(const Foo &other)
    {
        std::cout << "Copy constructor" << std::endl;
        data = new int { *other.data };
    }

    Foo(Foo &&other)
    {
        std::cout << "Move constructor" << std::endl;
        data = other.data;
        other.data = NULL;
    }

    Foo test(void) &
    {
        std::cout << "test(void) &" << std::endl;
        return Foo(*this);
    }

    Foo test(void) &&
    {
        std::cout << "test(void) &&" << std::endl;
        return Foo(std::move(*this));
    }

    int value(void)
    {
        return data ? (*data) : (-1);
    }

protected:
    int *data;
};

int main()
{
    Foo foo(42);

    std::cout << "---" << std::endl;

    std::cout << foo.test().value() << std::endl;
    std::cout << foo.value() << std::endl;

    std::cout << "---" << std::endl;

    std::cout << std::move(foo).test().value() << std::endl;
    std::cout << foo.value() << std::endl;

    std::cout << "---" << std::endl;

    std::cout << Foo(42).test().value() << std::endl;
}

Constructor
---
test(void) &
Copy constructor
42
42
---
test(void) &&
Move constructor
42
-1
---
Constructor
test(void) &&
Move constructor
42
Last edited on Aug 16, 2022 at 12:56pm
Aug 16, 2022 at 12:22pm
The method && mean that the member function can only be invoked on r-values (the && applies to this - similar to how const applies to this). This allows multiple member-function overloads based upon these ref-qualifies (const, &, &&) with different code/return for each.

However, you probably shouldn't use std::move() on L12. This can break return value optimisation (copy elision etc).
Last edited on Aug 16, 2022 at 4:07pm
Aug 16, 2022 at 1:14pm
std::move() is required; copy elision can't be applied to member objects.
Topic archived. No new replies allowed.