Paul writes:
> The below code compiles but crashes runtime with an unexpected termination.
> I'm not sure why but it has something to do with the scope of temporaries
> or something.
> Please could someone explain exactly what the problem is?
> Thank you.
> template <typename T> struct AndSpecification : Specification<T>
> {
> const Specification<T>& first;
> const Specification<T>& second;
>
> AndSpecification(const Specification<T>& first, const Specification<T>&
> second)
> : first(first), second(second) {}
You're grabbing references to these parameters and storing them in this
(template) class.
> auto spec2 = SizeSpecification(Size::large)&&
> ColorSpecification(Color::green);
You are constructing an instance of the AndSpecification template, using the
&& operator overload.
Both "SizeSpecification(Size::large)" and "ColorSpecification(Color::green)"
are temporary objects here, that get created when evaluating this expression.
And immediately destroyed after the expression gets evaluated.
Your instance of the AndSpecification template, thusly, ends up having
references to destroyed objects. They certainly exist when AndSpecification
gets constructured, and its constructor gets valid references to them. But
because the referenced objects are temporary objects, they get destroyed
soon thereafter.
Explicitly instantiating everything should work:
auto large=SizeSpecification(Size::large);
auto green=SizeSpecification(Size::green);
auto spec2 = large && green;
Now, both large and green objects will exist as long as the "spec2" object
exists, so the references to them, in spec2, remain valid.
Of course, this is not as convenient. But this is how your templates are
designed. Basically, if you store a reference somewhere, it's your
responsibility to make sure that the referenced objects still exist every
time you use that referenced object. C++ will not do it for you.
The only way to make sure that the object exist is store the object itself,
and not a reference to it. This is impossible to do, in your situation,
because the object themselves are abstract base classes.
The most common way this kind of concept gets implemented is with smart
pointers. And plenty of overloading.