Is make_unique in initializer list in copy constructor good purpose to not use noexcept specifier?
Asked Answered
P

5

6

I have a hurdle with noexcept specifier next to my copy constructor.

#include <memory>
#include <vector>

class Foo final {
 public:
  Foo() noexcept = default;
  Foo(const Foo& oth) : impl_(std::make_unique<Foo::Impl>()) {} // <---
  ~Foo() noexcept = default;

private:
  class Impl;
  std::unique_ptr<Impl> impl_;
};

class Foo::Impl {
  ...
 private:
  std::vector<int> some_data;
}

I'm not sure if I should put noexcept next to copy constructor while there is std::make_unique that can throw bad_alloc.

Any help will be apreciated!

Phospholipide answered 25/1, 2019 at 8:9 Comment(0)
V
4

The cpp coding guidelines are pretty clear about it in E.12: Use noexcept when exiting a function because of a throw is impossible or unacceptable

So you can use noexcept even if the call of that function/ctor could result in an exception, if that exception would - in your opinion - result in a not handleable state of your application.

Example from the guidelines:

vector<double> munge(const vector<double>& v) noexcept
{
    vector<double> v2(v.size());
    // ... do something ...
}

The noexcept here states that I am not willing or able to handle the situation where I cannot construct the local vector. That is, I consider memory exhaustion a serious design error (on par with hardware failures) so that I'm willing to crash the program if it happens.

So if a failed construction of Foo can be handled using a try-catch block without serious problems. Then you won't use a noexcept there.

Vinni answered 25/1, 2019 at 8:33 Comment(0)
S
3

Reflecting on some other answers: I wouldn't use noexcept if a function potentially throws, even if you don't care if your program will terminate if it eventually does. Because it will, if a function declared as noexcept throws. Declaring your function noexcept holds semantic information for the users of your class, they can depend on this information, which is essentially untrue in your case.

EDIT: I recommend you to read Item 14 of Scott Meyers' Effective Modern C++, it describes well the benefits of using noexcept and when to use it.

Stipe answered 25/1, 2019 at 8:43 Comment(0)
N
1

I believe that this really depends on the context. If you can reasonably handle that exception (and expect it be thrown), then you shouldn't mark it as noexcept. On the other hand if there is no way for your program to recover from the exception, you might as well mark it as noexcept.
I'd say that the second case true when the object to allocate is rather small (you won't be able to do a lot of exception handling if you can't allocate a single char). The first one should occur for really big objects (then you could postpone allocation for example). That being said, if you are copying a huge object... Why not move it instead?

Nitz answered 25/1, 2019 at 8:22 Comment(0)
B
1

Simple answer: Don't declare it noexcept, if you know it may throw and you do not have a really good reason to do so (for instance you want your application to call std::terminate if a copy cannot be made).

Ask yourself what the gain would be. Can the compiler optimize anything when it is noexcept? I do not see many cases where this would be the case (the most common case where I see optimizations is for a move as then the std-library containers can use it - they check if a move operation is noexcept so they can keep their guarantees). The other place where you might use it is when you want to document that a function cannot throw. This is obviosuly not the case here.

On the other hand, you will not be able to recover from a possible exception anymore and your program will terminate.

So in summery, you do not gain anything, but you might lose something.

Also consult the core guidelines:

E.12: Use noexcept when exiting a function because of a throw is impossible or unacceptable

The title of this rule may be a little bit confusing. It says that you should declare a function as noexcept, if

  • it does not throw or
  • you don't care in case of an exception. You are willing to crash the program because you can not handle an exception such as std::bad_alloc due to memory exhaustion.

It's not a good idea to throw an exception if you are the direct owner of an object.

By the way, the following special member functions are implicitly noexcept:

  • Default constructor
  • Destructor (as far as I know even if you explicitly throw in it)
  • Move and copy constructor
  • Move and copy assignment operator
Blackshear answered 25/1, 2019 at 8:25 Comment(2)
About destructor: In practice, implicit destructors are noexcept unless the class is "poisoned" by a base or member whose destructor is noexcept(false)Vinni
@Vinni Alright, I think that is compliant with what I wrote. Thanks for clearing it upBlackshear
S
1

When to use noexcept

"best practices"

Having to think about whether or not I need to append noexcept after every function declaration would greatly reduce programmer productivity (and frankly, would be a pain).

Well then use it when it's obvious that the function will never throw.

When can I realistically expect to observe a performance improvement after using noexcept? [...] Personally, I care about noexcept because of the increased freedom provided to the compiler to safely apply certain kinds of optimizations.

It seems like the biggest optimization gains are from user optimizations, not compiler ones due to the possibility of checking noexcept and overloading on it. Most compilers follow a no-penalty-if-you-don't-throw exception handling method so I doubt it would change much (or anything) on the machine code level of your code, although perhaps reduce the binary size by removing the handling code.

Using noexcept in the big 4 (constructors, assignment, not destructors as they're already noexcept) will likely cause the best improvements as noexcept checks are 'common' in template code such as in std containers. For instance, std::vector won't use your class's move unless it's marked noexcept (or the compiler can deduce it otherwise).

Software answered 25/1, 2019 at 8:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.