2

I am a bit confused about new rules about copy elision and actually I am not even sure if it applies in this case. I have this:

template <typename T> struct foo {
    T t;
    foo(const T& t) : t(t) {}
    ~foo() { std::cout << "destructor \n"; }
}

template <typename T> foo<T> make_foo(const T& t) { return {t}; }

where make_foo is only to allow deducing the parameter (in the real code t is a lambda, but I left it out here for the sake of simplicity, or rather for the sake of confusion, sorry for that) as in

auto x = make_foo(123);

Now I need to be absolutely sure that foos destructor is called exactly once: when x goes out of scope. I am afraid this is a unclear-what-you-are-asking question, but if it is that obvious that there wont be any temporary foo, that would be answer enough ;).

In C++11, can I be certain that there wont be a temporary foo in make_foo that will be destroyed? The destructor should be called only when x goes out of scope.

As correctly pointed out in a comment, this question is the Y part of a XY question and the X part is that I want to implement some end of scope functionality. The destructor of foo has some side effects (in the example the cout) that should be called at the end of scope of x but not in make_foo in case there would be some temporary foo.

463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185
  • 1
    This has the smells of an XY problem... What is the *real* problem you have? Why do you need the destructor to be called only once? And of course a destructor for an ***object*** will be called only once, otherwise the compiler is buggy. – Some programmer dude Mar 20 '18 at 08:36
  • 1
    A destructor is called only once for each object, that is guaranteed! If you get a copy of your object, you get 2 calls, if you move, your destructor must be aware that the moved object is "empty" and if the object is elided you see one call. So what is the underlaying problem? – Klaus Mar 20 '18 at 08:40
  • 1
    I think in C++11 copy elision even for nameless temporary only allowed not mandated. [copy elision](http://en.cppreference.com/w/cpp/language/copy_elision) It is mandated since c++17. – Yola Mar 20 '18 at 08:42
  • There are pretty strict rules about [Copy elision](http://en.cppreference.com/w/cpp/language/copy_elision) but it is guaranteed only in c++17. – Smit Ycyken Mar 20 '18 at 08:42
  • 1
    @Yola, are you reading my mind?=) – Smit Ycyken Mar 20 '18 at 08:43
  • But in C++17 you will have automatic deduction guides, so you will not need such a construction. – Yola Mar 20 '18 at 08:44
  • So the answer to the question is definitely [here](https://stackoverflow.com/questions/10081429/when-is-a-c-destructor-called) – Smit Ycyken Mar 20 '18 at 08:47
  • Possible duplicate of [When is a C++ destructor called?](https://stackoverflow.com/questions/10081429/when-is-a-c-destructor-called) – Smit Ycyken Mar 20 '18 at 08:49
  • 1
    @Someprogrammerdude hm yes, it really is a xy, I want to have some end of scope functionality. foos destructor has some side effects that should run only in the scope of `x` not in `make_foo` in case there would be some temporary `foo`. Will update the quesiton – 463035818_is_not_an_ai Mar 20 '18 at 08:50
  • @user463035818 : You need a way to create an "empty" `foo`, and then define a move constructor which leaves the source empty. Alternatively, drop `make_foo` and write `foo x{123};` – Martin Bonner supports Monica Mar 20 '18 at 08:53
  • 1
    @MartinBonner urks, I made almost all the mistakes I usually point out on other questions, first it was a xy, then the example isnt accurate enough (in the real code `T` is a lambda, so I need the parameter deduction) and last but not least if I had read my books more carefully I would know the answer already ;) – 463035818_is_not_an_ai Mar 20 '18 at 08:55
  • @SmitYcyken not really the best duplicate. I know more or less when a destructor is called, what makes me unsure is whether there is a temporary `foo` in `make_foo` whose destructor is called – 463035818_is_not_an_ai Mar 20 '18 at 08:58
  • @user463035818 :-). Yes, I can see a lambda complicates the issue. – Martin Bonner supports Monica Mar 20 '18 at 08:58
  • @Yola if your comment "I think in C++11 copy elision even for nameless temporary only allowed not mandated. copy elision It is mandated since c++17.:" really applies in that case, this would be the answer I was looking for – 463035818_is_not_an_ai Mar 20 '18 at 09:02
  • Are you looking for scope guards? – Passer By Mar 20 '18 at 09:04
  • @PasserBy yes, and I guess I should be using something from boost (not aware of something in C++11), but while trying to roll my own I hit a point of ignorance (my own of course) and I wanted to understand it... – 463035818_is_not_an_ai Mar 20 '18 at 09:07
  • For a scope guard, you would just make the class moveable but not copyable. I would suggest also taking the `T` by value and moving it into `t` – Caleth Mar 20 '18 at 09:37
  • 1
    _"foos destructor has some side effects that should run only in the scope of x"_ Honestly that sounds like a dangerous design then. Either make the class non-copyable, or make it safe to copy. – Lightness Races in Orbit Mar 20 '18 at 11:31
  • @LightnessRacesinOrbit yes, making it non-copyable is probably the best solution – 463035818_is_not_an_ai Mar 20 '18 at 12:18
  • @user463035818: I think so! – Lightness Races in Orbit Mar 20 '18 at 12:19

3 Answers3

2

In C++11 copy elision even for nameless temporary only allowed not mandated. It's described here copy elision. It is mandated since C++17.

Also in C++17 you will have automatic deduction guides, so you will not need such a construction.

And you can test your compiler, because most of modern compilers will elide copying here.

Yola
  • 18,496
  • 11
  • 65
  • 106
  • Also to make sure that destructor will not be called for unnamed object you can just bind return value to a const reference. – toozyfuzzy Mar 20 '18 at 09:12
  • @toozyfuzzy that will create a dangling reference, which is worse – M.M Mar 20 '18 at 09:13
  • @M.M It won't. https://stackoverflow.com/questions/11560339/returning-temporary-object-and-binding-to-const-reference – toozyfuzzy Mar 20 '18 at 09:17
  • @toozyfuzzy only if const reference is local – Yola Mar 20 '18 at 09:20
  • 1
    @toozyfuzzy sorry, I thought you were suggesting changing the function to return by const reference. You could post your comment as an answer – M.M Mar 20 '18 at 09:23
  • @toozyfuzzy and it will be *const* reference. – Yola Mar 20 '18 at 09:29
  • 1
    @Yola an rvalue reference could be used for non-const (`auto&& x = make_foo(123);`) – M.M Mar 20 '18 at 09:34
  • @toozyfuzzy as I understand the article you linked it takes about the temporary that gets returned, however, once the value is returned from `make_foo` I have no doubts what happens with it – 463035818_is_not_an_ai Mar 20 '18 at 09:37
1

Since C++17 there is guaranteed to be no temporary.

In C++14 and earlier, there must be an accessible copy/move constructor, and it is optional to the compiler whether or not there is actually a temporary.

As far as I'm aware, the only compiler that would actually manifest a temporary is older versions of MSVC in Debug mode.

M.M
  • 138,810
  • 21
  • 208
  • 365
1

In your case to be sure that destructor will not be called for unnamed object you can bind return value to const reference. To clarify what happens if we not rely on copy elision:

template <typename T> foo<T> make_foo(const T& t) { return {t}; }

In this function, return object will not be constructed in scope of that function. It will create temporary unnamed object out of the scope. If you will bind return value to a new named object move constructor will be called(or copy if move is not defined) to create you new object from returned temporary. However if you bind returned temporary to const reference it will be strictly bound to that reference and no new objects will be constructed and temporary will not be destructed till that reference is out of the scope.

EDIT: To not mislead you. Constructor for temporary called in function scope, but lifetime of that temporary will indeed be prolonged to lifetime of const reference

If you need more info you can check this answer. It refers to the C++ standard.

Does a const reference prolong the life of a temporary?

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
toozyfuzzy
  • 1,080
  • 1
  • 9
  • 20