简体   繁体   中英

unordered_set::find and noexcept

I have the following unordered_set:

class ArtifactImpl{...};

class ArtifactSetKeyOps
{
  public:
    std::size_t operator()(const ArtifactImpl& artifact) const noexcept;
    bool operator()(const ArtifactImpl& lhs, const ArtifactImpl& rhs) const noexcept;
};

std::unordered_set<ArtifactImpl,
  ArtifactSetKeyOps,ArtifactSetKeyOps> artifactSet_;

Note that my hash and predicate template argument/s have a noexcept specification.

  • Would it be possible for unordered_set::find to throw?

I'm aware that find isn't marked noexcept, but in practice find should not perform allocations...

  • Would it be acceptable to call find from a noexcept marked function?

eg:

const ArtifactImpl& foo() noexcept
{
  auto pos = artifactSet_.find(key);
  //etc...
}

Even though foo() here above goes against find's noexcept specifications, I'm wanting to know whether the specification holds practically iff one provides one's own noexcept hash and comparison.

It could throw if you hash function (which it must call) throws. For that reason alone I wouldn't use it. But more importantly, I wouldn't use the noexcept specifier because it goes against the find function's specifications.

tl;dr: noexcept seems useful only for move constructor/assignment operator.

If you would dare to accept cplusplus.com or even me as sufficient authority: it does not throw any exception, unlessan aspect does - such as the allocator or a comparator throws, nothing throws.
(I should probably go dig out the relevant ISO passages now...)

However, as to my understanding, noexcept isn't particulary useful here:

Andrzej has a detailed explanation on his blog , from which I (mis)quote the relevant parts here:

  • noexcept doesn't buy much if the implementation throws despite the specification
  • it doesn't help with implementing a no-throw exception safety guarantee
  • and it is unlikely to enable significant optimizations except in one case, related to move semantics:

for certain functions, like vector<T>::push_back using move constructors/assignments of T instead of copy constructors/assignments can dramatically increase performance. However, if this move constructor/assignment can potentially throw, push_back would loose the strong exception safety guarantee. In order to take an advantage of the move operations (where possible) and at the same time keep the strong guarantee, there needs to be a way to determine if a given move operation is potentially throwing or not, and use it or alternatively fall back to good old copying. This is exactly what function std::move_if_noexcept does, but it needs T's operations to be marked as noexcept where appropriate.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM