0

I wonder if it is possible to use C++11's noexcept operator to define the noextcept specifier of e. g. a destructor that calls a method of another class (e. g. std::allocator::deallocate):

template <class DelegateAllocator = std::allocator<uint8_t>>
class MyAllocator final {
 public:
  using allocator_type = DelegateAllocator;

  // ...

  ~MyAllocator() noexcept(noexcept(/* what to use */))) {
    if (memory_ != nullptr) {
      allocator_.deallocate(memory_, length_);
    }
  }

private: 
 allocator_type allocator_;   
 uint8_t* memory_;
 // ...
};

Questions: What is the best solution to define noexcept dependent to the used methods of a delegated type (e. g. std::allocator)? What has to be done - when possible - to use methods of a delegated type when different overloads exist (e. g. how would I use a specific deallocate implementation when not only one is provided)?

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
Sonic78
  • 680
  • 5
  • 19

2 Answers2

1

In this is as easy as:

~MyAllocator() noexcept(noexcept(std::declval<allocator_type&>().deallocate( memory_, allocator_ ))) {
  if (memory_ != nullptr) {
    allocator_.deallocate(memory_, length_);
  }
}

Live example.

But in it is a pain to do "properly":

 ~MyAllocator() noexcept(noexcept(std::declval<allocator_type&>().deallocate( std::declval<uint8_t*&>(), std::declval<std::size_t&>() ))) {
    if (memory_ != nullptr) {
      allocator_.deallocate(memory_, length_);
    }
  }

Live example.

So, upgrade to .

You can also do it hackily:

 ~MyAllocator() noexcept(noexcept(std::declval<allocator_type&>().deallocate( (uint8_t*)nullptr,1 ))) {

but you have to be careful, because passing nullptr_t could get you the wrong answer (hence the above cast from nullptr to uint8_t*, and avoiding using 0 as a literal).

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • Thank you! One question: why `std::declval().deallocate( std::declval(), std::declval() )` and not `std::declval().deallocate( std::declval(), std::declval() )`? – Sonic78 Oct 23 '18 at 11:25
  • 1
    @Sonic78 Because we are passing lvalues and not rvalues. In theory an allocator could distinguish between them and throw for rvalues but not for lvalues, or vice versa. It would be an insane allocator, but in generic code you code defensively. – Yakk - Adam Nevraumont Oct 23 '18 at 12:49
0

This stack overflow answer pointed me to one solution:

~StackAllocator() noexcept(noexcept(std::declval<allocator_type>().*&allocator_type::deallocate)) {
    if (memory_ != nullptr) {
      allocator_.deallocate(memory_, length_);
    }
}

This solution works as long as only one overload for the method exists. If anybody can provide a better solution (better to read, reusable ...) or a solution that works also with methods that have overloads please provide it.

Edit/Update: A colleague and the answer from Yakk below provided a better solution that also covers different overloads:

~StackAllocator() noexcept(noexcept(std::declval<allocator_type>().deallocate(std::declval<std::uint8_t*>(), std::declval<std::size_t>()))) 
{
    if (memory_ != nullptr) {
        allocator_.deallocate(memory_, length_);
    }
}
Sonic78
  • 680
  • 5
  • 19
  • https://stackoverflow.com/questions/17874489/disambiguate-overloaded-member-function-pointer-being-passed-as-template-paramet – rustyx Oct 22 '18 at 16:14