Commit 15de336d authored by Adam Ernst's avatar Adam Ernst Committed by Facebook GitHub Bot

@build-break revert D23279680 & D23349316

Summary:
Breaks builds.

overriding_review_checks_triggers_an_audit_and_retroactive_review
Oncall Short Name: fbobjc_sheriff

fbshipit-source-id: 8b6437a9e87e4888a6b7a85637232308412cf416
parent 38e819bc
......@@ -149,10 +149,6 @@ class ConcurrentHashMap {
Atom,
Mutex,
Impl>;
template <typename K, typename T>
using EnableHeterogeneousFind = std::enable_if_t<
detail::EligibleForHeterogeneousFind<KeyType, HashFn, KeyEqual, K>::value,
T>;
float load_factor_ = SegmentT::kDefaultLoadFactor;
......@@ -169,27 +165,6 @@ class ConcurrentHashMap {
typedef KeyEqual key_equal;
typedef ConstIterator const_iterator;
private:
template <typename K, typename T>
using EnableHeterogeneousInsert = std::enable_if_t<
::folly::detail::
EligibleForHeterogeneousInsert<KeyType, HashFn, KeyEqual, K>::value,
T>;
template <typename K>
using IsIter = std::is_same<ConstIterator, remove_cvref_t<K>>;
template <typename K, typename T>
using EnableHeterogeneousErase = std::enable_if_t<
::folly::detail::EligibleForHeterogeneousFind<
KeyType,
HashFn,
KeyEqual,
std::conditional_t<IsIter<K>::value, KeyType, K>>::value &&
!IsIter<K>::value,
T>;
public:
/*
* Construct a ConcurrentHashMap with 1 << ShardBits shards, size
* and max_size given. Both size and max_size will be rounded up to
......@@ -264,11 +239,14 @@ class ConcurrentHashMap {
return true;
}
ConstIterator find(const KeyType& k) const { return findImpl(k); }
template <typename K, EnableHeterogeneousFind<K, int> = 0>
ConstIterator find(const K& k) const {
return findImpl(k);
ConstIterator find(const KeyType& k) const {
auto segment = pickSegment(k);
ConstIterator res(this, segment);
auto seg = segments_[segment].load(std::memory_order_acquire);
if (!seg || !seg->find(res.it_, k)) {
res.segment_ = NumShards;
}
return res;
}
ConstIterator cend() const noexcept { return ConstIterator(NumShards); }
......@@ -281,12 +259,13 @@ class ConcurrentHashMap {
std::pair<ConstIterator, bool> insert(
std::pair<key_type, mapped_type>&& foo) {
return insertImpl(std::move(foo));
}
template <typename Key, EnableHeterogeneousInsert<Key, int> = 0>
std::pair<ConstIterator, bool> insert(std::pair<Key, mapped_type>&& foo) {
return insertImpl(std::move(foo));
auto segment = pickSegment(foo.first);
std::pair<ConstIterator, bool> res(
std::piecewise_construct,
std::forward_as_tuple(this, segment),
std::forward_as_tuple(false));
res.second = ensureSegment(segment)->insert(res.first.it_, std::move(foo));
return res;
}
template <typename Key, typename Value>
......@@ -395,26 +374,24 @@ class ConcurrentHashMap {
return item.first->second;
}
template <typename Key, EnableHeterogeneousInsert<Key, int> = 0>
const ValueType operator[](const Key& key) {
auto item = insert(key, ValueType());
return item.first->second;
}
const ValueType at(const KeyType& key) const { return atImpl(key); }
template <typename K, EnableHeterogeneousFind<K, int> = 0>
const ValueType at(const K& key) const {
return atImpl(key);
const ValueType at(const KeyType& key) const {
auto item = find(key);
if (item == cend()) {
throw_exception<std::out_of_range>("at(): value out of range");
}
return item->second;
}
// TODO update assign interface, operator[], at
size_type erase(const key_type& k) { return eraseImpl(k); }
template <typename K, EnableHeterogeneousErase<K, int> = 0>
size_type erase(const K& k) {
return eraseImpl(k);
size_type erase(const key_type& k) {
auto segment = pickSegment(k);
auto seg = segments_[segment].load(std::memory_order_acquire);
if (!seg) {
return 0;
} else {
return seg->erase(k);
}
}
// Calls the hash function, and therefore may throw.
......@@ -432,24 +409,15 @@ class ConcurrentHashMap {
k, [&expected](const ValueType& v) { return v == expected; });
}
template <typename K, EnableHeterogeneousErase<K, int> = 0>
size_type erase_if_equal(const K& k, const ValueType& expected) {
return erase_key_if(
k, [&expected](const ValueType& v) { return v == expected; });
}
// Erase if predicate evaluates to true on the existing value
template <typename Predicate>
size_type erase_key_if(const key_type& k, Predicate&& predicate) {
return eraseKeyIfImpl(k, std::forward<Predicate>(predicate));
}
template <
typename K,
typename Predicate,
EnableHeterogeneousErase<K, int> = 0>
size_type erase_key_if(const K& k, Predicate&& predicate) {
return eraseKeyIfImpl(k, std::forward<Predicate>(predicate));
auto segment = pickSegment(k);
auto seg = segments_[segment].load(std::memory_order_acquire);
if (!seg) {
return 0;
}
return seg->erase_key_if(k, std::forward<Predicate>(predicate));
}
// NOT noexcept, initializes new shard segments vs.
......@@ -573,60 +541,7 @@ class ConcurrentHashMap {
};
private:
template <typename K>
ConstIterator findImpl(const K& k) const {
auto segment = pickSegment(k);
ConstIterator res(this, segment);
auto seg = segments_[segment].load(std::memory_order_acquire);
if (!seg || !seg->find(res.it_, k)) {
res.segment_ = NumShards;
}
return res;
}
template <typename K>
const ValueType atImpl(const K& k) const {
auto item = find(k);
if (item == cend()) {
throw_exception<std::out_of_range>("at(): key not in map");
}
return item->second;
}
template <typename Key>
std::pair<ConstIterator, bool> insertImpl(std::pair<Key, mapped_type>&& foo) {
auto segment = pickSegment(foo.first);
std::pair<ConstIterator, bool> res(
std::piecewise_construct,
std::forward_as_tuple(this, segment),
std::forward_as_tuple(false));
res.second = ensureSegment(segment)->insert(res.first.it_, std::move(foo));
return res;
}
template <typename K>
size_type eraseImpl(const K& k) {
auto segment = pickSegment(k);
auto seg = segments_[segment].load(std::memory_order_acquire);
if (!seg) {
return 0;
} else {
return seg->erase(k);
}
}
template <typename K, typename Predicate>
size_type eraseKeyIfImpl(const K& k, Predicate&& predicate) {
auto segment = pickSegment(k);
auto seg = segments_[segment].load(std::memory_order_acquire);
if (!seg) {
return 0;
}
return seg->erase_key_if(k, std::forward<Predicate>(predicate));
}
template <typename K>
uint64_t pickSegment(const K& k) const {
uint64_t pickSegment(const KeyType& k) const {
auto h = HashFn()(k);
// Use the lowest bits for our shard bits.
//
......
......@@ -16,7 +16,6 @@
#pragma once
#include <folly/container/HeterogeneousAccess.h>
#include <folly/container/detail/F14Mask.h>
#include <folly/lang/Exception.h>
#include <folly/lang/Launder.h>
......@@ -254,10 +253,10 @@ class alignas(64) BucketTable {
bool empty() { return size() == 0; }
template <typename MatchFunc, typename K, typename... Args>
template <typename MatchFunc, typename... Args>
bool insert(
Iterator& it,
const K& k,
const KeyType& k,
InsertType type,
MatchFunc match,
hazptr_obj_cohort<Atom>* cohort,
......@@ -266,10 +265,10 @@ class alignas(64) BucketTable {
it, k, type, match, nullptr, cohort, std::forward<Args>(args)...);
}
template <typename MatchFunc, typename K, typename... Args>
template <typename MatchFunc, typename... Args>
bool insert(
Iterator& it,
const K& k,
const KeyType& k,
InsertType type,
MatchFunc match,
Node* cur,
......@@ -334,8 +333,7 @@ class alignas(64) BucketTable {
oldbuckets->retire(concurrenthashmap::HazptrTableDeleter(oldcount));
}
template <typename K>
bool find(Iterator& res, const K& k) {
bool find(Iterator& res, const KeyType& k) {
auto& hazcurr = res.hazptrs_[1];
auto& haznext = res.hazptrs_[2];
auto h = HashFn()(k);
......@@ -357,8 +355,8 @@ class alignas(64) BucketTable {
return false;
}
template <typename K, typename MatchFunc>
std::size_t erase(const K& key, Iterator* iter, MatchFunc match) {
template <typename MatchFunc>
std::size_t erase(const KeyType& key, Iterator* iter, MatchFunc match) {
Node* node{nullptr};
auto h = HashFn()(key);
{
......@@ -605,10 +603,10 @@ class alignas(64) BucketTable {
DCHECK(buckets);
}
template <typename MatchFunc, typename K, typename... Args>
template <typename MatchFunc, typename... Args>
bool doInsert(
Iterator& it,
const K& k,
const KeyType& k,
InsertType type,
MatchFunc match,
Node* cur,
......@@ -1145,10 +1143,10 @@ class alignas(64) SIMDTable {
bool empty() { return size() == 0; }
template <typename MatchFunc, typename K, typename... Args>
template <typename MatchFunc, typename... Args>
bool insert(
Iterator& it,
const K& k,
const KeyType& k,
InsertType type,
MatchFunc match,
hazptr_obj_cohort<Atom>* cohort,
......@@ -1198,10 +1196,10 @@ class alignas(64) SIMDTable {
return true;
}
template <typename MatchFunc, typename K, typename... Args>
template <typename MatchFunc, typename... Args>
bool insert(
Iterator& it,
const K& k,
const KeyType& k,
InsertType type,
MatchFunc match,
Node* cur,
......@@ -1254,8 +1252,7 @@ class alignas(64) SIMDTable {
rehash_internal(folly::nextPowTwo(new_chunk_count), cohort);
}
template <typename K>
bool find(Iterator& res, const K& k) {
bool find(Iterator& res, const KeyType& k) {
auto& hazz = res.hazptrs_[1];
auto h = HashFn()(k);
auto hp = splitHash(h);
......@@ -1287,8 +1284,8 @@ class alignas(64) SIMDTable {
return false;
}
template <typename K, typename MatchFunc>
std::size_t erase(const K& key, Iterator* iter, MatchFunc match) {
template <typename MatchFunc>
std::size_t erase(const KeyType& key, Iterator* iter, MatchFunc match) {
auto h = HashFn()(key);
const HashPair hp = splitHash(h);
......@@ -1391,9 +1388,8 @@ class alignas(64) SIMDTable {
static size_t probeDelta(HashPair hp) { return 2 * hp.second + 1; }
// Must hold lock.
template <typename K>
Node* find_internal(
const K& k,
const KeyType& k,
const HashPair& hp,
Chunks* chunks,
size_t ccount,
......@@ -1422,10 +1418,10 @@ class alignas(64) SIMDTable {
return nullptr;
}
template <typename MatchFunc, typename K, typename... Args>
template <typename MatchFunc, typename... Args>
bool prepare_insert(
Iterator& it,
const K& k,
const KeyType& k,
InsertType type,
MatchFunc match,
hazptr_obj_cohort<Atom>* cohort,
......@@ -1650,8 +1646,7 @@ class alignas(64) ConcurrentHashMapSegment {
bool empty() { return impl_.empty(); }
template <typename Key>
bool insert(Iterator& it, std::pair<Key, mapped_type>&& foo) {
bool insert(Iterator& it, std::pair<key_type, mapped_type>&& foo) {
return insert(it, std::move(foo.first), std::move(foo.second));
}
......@@ -1751,10 +1746,10 @@ class alignas(64) ConcurrentHashMapSegment {
return res;
}
template <typename MatchFunc, typename K, typename... Args>
template <typename MatchFunc, typename... Args>
bool insert_internal(
Iterator& it,
const K& k,
const KeyType& k,
InsertType type,
MatchFunc match,
Args&&... args) {
......@@ -1762,10 +1757,10 @@ class alignas(64) ConcurrentHashMapSegment {
it, k, type, match, cohort_, std::forward<Args>(args)...);
}
template <typename MatchFunc, typename K, typename... Args>
template <typename MatchFunc, typename... Args>
bool insert_internal(
Iterator& it,
const K& k,
const KeyType& k,
InsertType type,
MatchFunc match,
Node* cur) {
......@@ -1775,24 +1770,21 @@ class alignas(64) ConcurrentHashMapSegment {
// Must hold lock.
void rehash(size_t bucket_count) { impl_.rehash(bucket_count, cohort_); }
template <typename K>
bool find(Iterator& res, const K& k) {
return impl_.find(res, k);
}
bool find(Iterator& res, const KeyType& k) { return impl_.find(res, k); }
// Listed separately because we need a prev pointer.
template <typename K>
size_type erase(const K& key) {
size_type erase(const key_type& key) {
return erase_internal(key, nullptr, [](const ValueType&) { return true; });
}
template <typename K, typename Predicate>
size_type erase_key_if(const K& key, Predicate&& predicate) {
template <typename Predicate>
size_type erase_key_if(const key_type& key, Predicate&& predicate) {
return erase_internal(key, nullptr, std::forward<Predicate>(predicate));
}
template <typename K, typename MatchFunc>
size_type erase_internal(const K& key, Iterator* iter, MatchFunc match) {
template <typename MatchFunc>
size_type
erase_internal(const key_type& key, Iterator* iter, MatchFunc match) {
return impl_.erase(key, iter, match);
}
......
......@@ -20,8 +20,6 @@
#include <memory>
#include <thread>
#include <folly/Traits.h>
#include <folly/container/test/TrackingTypes.h>
#include <folly/hash/Hash.h>
#include <folly/portability/GFlags.h>
#include <folly/portability/GTest.h>
......@@ -832,110 +830,6 @@ TYPED_TEST_P(ConcurrentHashMapTest, IteratorLoop) {
EXPECT_EQ(count, kNum);
}
namespace {
template <typename T, typename Arg>
using detector_find = decltype(std::declval<T>().find(std::declval<Arg>()));
template <typename T, typename Arg>
using detector_erase = decltype(std::declval<T>().erase(std::declval<Arg>()));
} // namespace
TYPED_TEST_P(ConcurrentHashMapTest, HeterogeneousLookup) {
using Hasher = folly::transparent<folly::hasher<folly::StringPiece>>;
using KeyEqual = folly::transparent<std::equal_to<folly::StringPiece>>;
using M = ConcurrentHashMap<std::string, bool, Hasher, KeyEqual>;
constexpr auto hello = "hello"_sp;
constexpr auto buddy = "buddy"_sp;
constexpr auto world = "world"_sp;
M map;
map.emplace(hello, true);
map.emplace(world, false);
auto checks = [hello, buddy](auto& ref) {
// find
EXPECT_TRUE(ref.end() == ref.find(buddy));
EXPECT_EQ(hello, ref.find(hello)->first);
// at
EXPECT_TRUE(ref.at(hello));
EXPECT_THROW(ref.at(buddy), std::out_of_range);
// invocability checks
static_assert(
!is_detected_v<detector_find, decltype(ref), int>,
"there shouldn't be a find() overload for this string map with an int param");
};
checks(map);
checks(folly::as_const(map));
}
TYPED_TEST_P(ConcurrentHashMapTest, HeterogeneousInsert) {
using Hasher = folly::transparent<folly::hasher<folly::StringPiece>>;
using KeyEqual = folly::transparent<std::equal_to<folly::StringPiece>>;
using P = std::pair<StringPiece, std::string>;
using CP = std::pair<const StringPiece, std::string>;
ConcurrentHashMap<std::string, std::string, Hasher, KeyEqual> map;
P p{"foo", "hello"};
StringPiece foo{"foo"};
StringPiece bar{"bar"};
map.insert("foo", "hello");
map.insert(foo, "hello");
// TODO(T31574848): the list-initialization below does not work on libstdc++
// versions (e.g., GCC < 6) with no implementation of N4387 ("perfect
// initialization" for pairs and tuples).
// StringPiece sp{"foo"};
// map.insert({sp, "hello"});
map.insert({"foo", "hello"});
map.insert(P("foo", "hello"));
map.insert(CP("foo", "hello"));
map.insert(std::move(p));
map.insert_or_assign("foo", "hello");
map.insert_or_assign(StringPiece{"foo"}, "hello");
map.erase(StringPiece{"foo"});
map.erase(foo);
map.erase("");
EXPECT_TRUE(map.empty());
map.insert("foo", "hello");
map.insert("bar", "world");
map.erase_if_equal(StringPiece{"foo"}, "hello");
map.erase_key_if(bar, [](const std::string& s) { return s == "world"; });
map.erase("");
EXPECT_TRUE(map.empty());
map.insert("foo", "baz");
EXPECT_TRUE(map.assign(foo, "hello2"));
EXPECT_TRUE(map.assign_if_equal("foo", "hello2", "hello"));
EXPECT_EQ(map[foo], "hello");
auto it = map.find(foo);
map.erase(it);
EXPECT_TRUE(map.empty());
map.try_emplace(foo);
map.try_emplace(foo, "hello");
map.try_emplace(StringPiece{"foo"}, "hello");
map.try_emplace(foo, "hello");
map.try_emplace(foo);
map.try_emplace("foo");
map.try_emplace("foo", "hello");
map.try_emplace("bar", /* count */ 20, 'x');
EXPECT_EQ(map[bar], std::string(20, 'x'));
map.emplace(StringPiece{"foo"}, "hello");
map.emplace("foo", "hello");
// invocability checks
static_assert(
!is_detected_v<detector_erase, decltype(map), int>,
"there shouldn't be an erase() overload for this string map with an int param");
}
REGISTER_TYPED_TEST_CASE_P(
ConcurrentHashMapTest,
MapTest,
......@@ -970,9 +864,7 @@ REGISTER_TYPED_TEST_CASE_P(
assignStressTest,
insertStressTest,
IteratorMove,
IteratorLoop,
HeterogeneousLookup,
HeterogeneousInsert);
IteratorLoop);
using folly::detail::concurrenthashmap::bucket::BucketTable;
......
......@@ -54,7 +54,7 @@ template <typename Policy>
class F14BasicMap {
template <typename K, typename T>
using EnableHeterogeneousFind = std::enable_if_t<
::folly::detail::EligibleForHeterogeneousFind<
EligibleForHeterogeneousFind<
typename Policy::Key,
typename Policy::Hasher,
typename Policy::KeyEqual,
......@@ -63,7 +63,7 @@ class F14BasicMap {
template <typename K, typename T>
using EnableHeterogeneousInsert = std::enable_if_t<
::folly::detail::EligibleForHeterogeneousInsert<
EligibleForHeterogeneousInsert<
typename Policy::Key,
typename Policy::Hasher,
typename Policy::KeyEqual,
......@@ -77,7 +77,7 @@ class F14BasicMap {
template <typename K, typename T>
using EnableHeterogeneousErase = std::enable_if_t<
::folly::detail::EligibleForHeterogeneousFind<
EligibleForHeterogeneousFind<
typename Policy::Key,
typename Policy::Hasher,
typename Policy::KeyEqual,
......@@ -395,7 +395,7 @@ class F14BasicMap {
private:
template <typename Arg>
using UsableAsKey = ::folly::detail::
using UsableAsKey =
EligibleForHeterogeneousFind<key_type, hasher, key_equal, Arg>;
public:
......@@ -953,7 +953,7 @@ class F14VectorMapImpl : public F14BasicMap<MapPolicyWithDefaults<
template <typename K, typename T>
using EnableHeterogeneousVectorErase = std::enable_if_t<
::folly::detail::EligibleForHeterogeneousFind<
EligibleForHeterogeneousFind<
Key,
Hasher,
KeyEqual,
......
......@@ -50,7 +50,7 @@ template <typename Policy>
class F14BasicSet {
template <typename K, typename T>
using EnableHeterogeneousFind = std::enable_if_t<
::folly::detail::EligibleForHeterogeneousFind<
EligibleForHeterogeneousFind<
typename Policy::Value,
typename Policy::Hasher,
typename Policy::KeyEqual,
......@@ -59,7 +59,7 @@ class F14BasicSet {
template <typename K, typename T>
using EnableHeterogeneousInsert = std::enable_if_t<
::folly::detail::EligibleForHeterogeneousInsert<
EligibleForHeterogeneousInsert<
typename Policy::Value,
typename Policy::Hasher,
typename Policy::KeyEqual,
......@@ -71,7 +71,7 @@ class F14BasicSet {
template <typename K, typename T>
using EnableHeterogeneousErase = std::enable_if_t<
::folly::detail::EligibleForHeterogeneousFind<
EligibleForHeterogeneousFind<
typename Policy::Value,
typename Policy::Hasher,
typename Policy::KeyEqual,
......@@ -311,7 +311,7 @@ class F14BasicSet {
private:
template <typename Arg>
using UsableAsKey = ::folly::detail::
using UsableAsKey =
EligibleForHeterogeneousFind<key_type, hasher, key_equal, Arg>;
public:
......@@ -718,7 +718,7 @@ class F14VectorSetImpl : public F14BasicSet<SetPolicyWithDefaults<
template <typename K, typename T>
using EnableHeterogeneousVectorErase = std::enable_if_t<
::folly::detail::EligibleForHeterogeneousFind<
EligibleForHeterogeneousFind<
typename Policy::Value,
typename Policy::Hasher,
typename Policy::KeyEqual,
......
......@@ -143,27 +143,6 @@ struct TransparentRangeHash {
}
};
template <
typename TableKey,
typename Hasher,
typename KeyEqual,
typename ArgKey>
struct EligibleForHeterogeneousFind
: Conjunction<
is_transparent<Hasher>,
is_transparent<KeyEqual>,
is_invocable<Hasher, ArgKey const&>,
is_invocable<KeyEqual, ArgKey const&, TableKey const&>> {};
template <
typename TableKey,
typename Hasher,
typename KeyEqual,
typename ArgKey>
using EligibleForHeterogeneousInsert = Conjunction<
EligibleForHeterogeneousFind<TableKey, Hasher, KeyEqual, ArgKey>,
std::is_constructible<TableKey, ArgKey>>;
} // namespace detail
template <typename T>
......
......@@ -43,14 +43,12 @@ class F14BasicMap : public std::unordered_map<K, M, H, E, A> {
using Super = std::unordered_map<K, M, H, E, A>;
template <typename K2, typename T>
using EnableHeterogeneousFind = std::enable_if_t<
::folly::detail::EligibleForHeterogeneousFind<K, H, E, K2>::value,
T>;
using EnableHeterogeneousFind =
std::enable_if_t<EligibleForHeterogeneousFind<K, H, E, K2>::value, T>;
template <typename K2, typename T>
using EnableHeterogeneousInsert = std::enable_if_t<
::folly::detail::EligibleForHeterogeneousInsert<K, H, E, K2>::value,
T>;
using EnableHeterogeneousInsert =
std::enable_if_t<EligibleForHeterogeneousInsert<K, H, E, K2>::value, T>;
template <typename K2>
using IsIter = Disjunction<
......@@ -59,7 +57,7 @@ class F14BasicMap : public std::unordered_map<K, M, H, E, A> {
template <typename K2, typename T>
using EnableHeterogeneousErase = std::enable_if_t<
::folly::detail::EligibleForHeterogeneousFind<
EligibleForHeterogeneousFind<
K,
H,
E,
......@@ -194,7 +192,7 @@ class F14BasicMap : public std::unordered_map<K, M, H, E, A> {
private:
template <typename Arg>
using UsableAsKey = ::folly::detail::
using UsableAsKey =
EligibleForHeterogeneousFind<key_type, hasher, key_equal, Arg>;
public:
......
......@@ -54,14 +54,12 @@ class F14BasicSet
private:
template <typename K, typename T>
using EnableHeterogeneousFind = std::enable_if_t<
::folly::detail::
EligibleForHeterogeneousFind<key_type, hasher, key_equal, K>::value,
EligibleForHeterogeneousFind<key_type, hasher, key_equal, K>::value,
T>;
template <typename K, typename T>
using EnableHeterogeneousInsert = std::enable_if_t<
::folly::detail::
EligibleForHeterogeneousInsert<key_type, hasher, key_equal, K>::value,
EligibleForHeterogeneousInsert<key_type, hasher, key_equal, K>::value,
T>;
template <typename K>
......@@ -71,7 +69,7 @@ class F14BasicSet
template <typename K, typename T>
using EnableHeterogeneousErase = std::enable_if_t<
::folly::detail::EligibleForHeterogeneousFind<
EligibleForHeterogeneousFind<
key_type,
hasher,
key_equal,
......@@ -103,7 +101,7 @@ class F14BasicSet
private:
template <typename Arg>
using UsableAsKey = ::folly::detail::
using UsableAsKey =
EligibleForHeterogeneousFind<key_type, hasher, key_equal, Arg>;
public:
......
......@@ -206,6 +206,27 @@ typename Container::size_type erase_if_impl(
return old_size - c.size();
}
template <
typename TableKey,
typename Hasher,
typename KeyEqual,
typename ArgKey>
struct EligibleForHeterogeneousFind
: Conjunction<
is_transparent<Hasher>,
is_transparent<KeyEqual>,
is_invocable<Hasher, ArgKey const&>,
is_invocable<KeyEqual, ArgKey const&, TableKey const&>> {};
template <
typename TableKey,
typename Hasher,
typename KeyEqual,
typename ArgKey>
using EligibleForHeterogeneousInsert = Conjunction<
EligibleForHeterogeneousFind<TableKey, Hasher, KeyEqual, ArgKey>,
std::is_constructible<TableKey, ArgKey>>;
} // namespace detail
} // namespace f14
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment