3
votes

Example of a code with a member initializer list.

#include <memory>

struct Throwable
{
    Throwable()
    {
        throw "Exception!";
    }
};

struct A
{
    A() : t(Throwable()), i(new int()) {}

    Throwable t;
    std::unique_ptr<int> i;
};

Can I have got a memory leak if there could be the following evaluation order?

  1. new int()
  2. Throwable()
  3. t()
  4. i()

What is the order in the Standard? We have some rules.

https://en.cppreference.com/w/cpp/language/initializer_list

3) Then, non-static data members are initialized in order of declaration in the class definition.

So t will be constructed before i.

https://en.cppreference.com/w/cpp/language/eval_order

9) Every value computation and side effect of the first (left) argument of the built-in comma operator , is sequenced before every value computation and side effect of the second (right) argument.

But member initializer list does not use all comma rules because of the previous reference. And it is not the comma operator (https://en.cppreference.com/w/cpp/language/operator_other#Built-in_comma_operator).

10) In list-initialization, every value computation and side effect of a given initializer clause is sequenced before every value computation and side effect associated with any initializer clause that follows it in the brace-enclosed comma-separated list of initalizers.

And https://en.cppreference.com/w/cpp/language/list_initialization

List initialization is performed in the following situations:

5) in a member initializer list of a constructor if braced-init-list is used

I have another case.

Could you provide the rule which defines the order of arguments evaluation in a member initializer list?

2
order of declaration ?Vivick
std::make_unique would avoid any "risk".Jarod42
'Order of evaluation'. I need the answer in terms of "sequenced-before" (en.cppreference.com/w/cpp/language/eval_order).Iurii Popov
@JesperJuhl: Because the standard is Byzantine, and this is from someone who actually managed to mostly comprehend the C++ object model as it is defined therein. For example, I would have expected to find the answer to this question in [class.base.init] where member initializer lists are defined, but it wasn't there. It was apparently in [intro.execution], 10 chapters earlier. Why? Because it happened to be covered by a general statement rather than a specific one. Information in the standard is usually sufficiently spread out that asking questions about it is not unreasonable.Nicol Bolas
@JesperJuhl standardese is not necessarily simple and obvious. I know it took me quite a while to get to a point where I would have some confidence in my reading of the standard. Even after many years, I still find it hard at times to be sure that my interpretation is correct. You can see from his question that he did make an effort to try find an answer. While "RTFM" may be a valid response to many a question, I don't think "look it up in the standard" is a useful response to a legitimate C++ question, particularly one that explicitly asked about what the standard says…Michael Kenzel

2 Answers

3
votes

No, this evaluation order that would potentially cause you to leak is guaranteed not to happen. The expression within each mem-initializer is a full-expression ([intro.execution] §5.3). And [intro.execution] §9 tells us

Every value computation and side effect associated with a full-expression is sequenced before every value computation and side effect associated with the next full-expression to be evaluated.

So in other words, the evaluation of each expression used to initialize a member is completed before evaluation of the expression for the next member starts.

0
votes

Can I have got a memory leak if there could be the following evaluation order?

The answer is No,

because order of initialization of non-static members is defined as same as order of members declaration within class definition, not related to order in member initializer list, that is because destruction need to be executed in reverse order of construction, in order to achieve that, it has to be rule to follow when destruction start. Regarding that, cause you have declared member of type throwable before any other member, it will be constructed first and throw before heap allocation happened. There is no leak.

But if you declared member of type throwable after unique_pointer declaration, in initialization process heap allocation would happened first, and after that, throwing exception from throwable constructor, will stop A construction, and even if catched, exception will be re-thrown after exception handler execution, but following rule that any constructed class member will be destructed as well, resource management capabilities of unique_pointer will prevent leak, in this scenario as well.

I hope this little observation helps.