22

Take the following example code:

void test(const Item& item = Item()) {
   ...
}

Assume that, once item has been passed to the function, this cannot throw.

The question is: the function should be marked noexcept or noexcept(noexcept(Item()))?

IHMO, the former should be ok, but I am not sure. A quotation from the standard would be very appreciated!

Rakete1111
  • 47,013
  • 16
  • 123
  • 162
dodomorandi
  • 1,143
  • 1
  • 11
  • 18

1 Answers1

13

Default arguments are shortcut notations for the caller of function. So, when the function executes, the construction is already complete.

Thus, noexcept should be sufficient.

In the standard [dcl.fct.default] states:

If an initializer-clause is specified in a parameter-declaration this initializer-clause is used as a default argument. Default arguments will be used in calls where trailing arguments are missing.

Example: the declaration void point(int = 3, int = 4); declares a function that can be called with zero, one, or two arguments of type int. It can be called in any of these ways: point(1,2); point(1); point(); The last two calls are equivalent to point(1,4) and point(3,4) , respectively.

Also there is a note (in [intro.execution] Program execution):

Subexpressions involved in evaluating default arguments (8.3.6) are considered to be created in the expression that calls the function, not the expression that defines the default argument

Barry
  • 286,269
  • 29
  • 621
  • 977
Philipp Claßen
  • 41,306
  • 31
  • 146
  • 239
  • 4
    That's exactly what I was thinking. Can you find something (even from cppreference would be ok) to prove that? My doubts are related to noexcept forwarding: if this function is noexcept, noexcept(test) would be true, but it would be misleading from a caller point of view. – dodomorandi May 10 '18 at 13:02
  • @dodomorandi From the language, the caller has already created the arguments. The noexcept specifier is only for the function itself. That the construction might throw is therefore not relevant for the function itself. – Philipp Claßen May 10 '18 at 13:09
  • That note is gone from the latest draft :( – Rakete1111 May 10 '18 at 13:09
  • I accepted you answer because you perfectly replied my question. However, I am editing the question because I thought about a case that I would like you to comment, if you want. – dodomorandi May 10 '18 at 13:10
  • @Rakete1111 The note is from "1.9 Program execution" in the linked PDF of the draft that I was using. – Philipp Claßen May 10 '18 at 13:11
  • @PhilippClaßen ok, what I was trying to do was to get the noexcept state of a wrapper function. What I always forget is that the type default parameter cannot be omitted in a `decltype` expression -- therefore there should not be a way to get a misleading `noexcept` value from generic code. If I am wrong, please let me know. – dodomorandi May 10 '18 at 13:40
  • 1
    This is correct, but I'll point out that Herb Sutter once strongly advised against a similar case (passing a type whose copy c'tor may throw – by value), because it can easily mislead the caller, as dodomorandi pointed out. On the other hand, `noexcept(Item())` is stricter than required when the argument has already been constructed. This may be another case of "default arguments considered harmful" and I recommend using two overloads instead. – Arne Vogel May 10 '18 at 18:04
  • I think the normative text is in [\[expr.call\]/7](http://eel.is/c++draft/expr.call#7.sentence-5). cc @Rakete1111 – bogdan May 19 '18 at 17:07