Commit 6c21531d authored by Nathan Bronson's avatar Nathan Bronson Committed by Facebook GitHub Bot

callWithExtractedKey fix for old libstdc++

Summary:
libstdc++ versions that don't include N4387 ("perfect initialization" for
pairs and tuples) can't use the two-arg pair constructor as an emplace
target when the emplace args don't exactly match the destination types.
(libstdc++.so.6.0.22 is the first one that contains the improvement,
which corresponds to gcc 6.1.0).  This diff guards the optimization added
in D21419822 (https://github.com/facebook/folly/commit/7d618e4fde39b44e0952880653e10ec97400583f) with an enable_if test to avoid the problem, and includes
the template parameter changes necessary to get the target types to the
test site.

(Note: this ignores all push blocking failures!)

Differential Revision: D21766560

fbshipit-source-id: f8b2a0230504237f976e14b0740bb31bc7a57b77
parent 67ed8482
...@@ -414,7 +414,8 @@ class F14BasicMap { ...@@ -414,7 +414,8 @@ class F14BasicMap {
public: public:
template <typename... Args> template <typename... Args>
std::pair<iterator, bool> emplace(Args&&... args) { std::pair<iterator, bool> emplace(Args&&... args) {
auto rv = folly::detail::callWithExtractedKey<key_type, UsableAsKey>( auto rv =
folly::detail::callWithExtractedKey<key_type, mapped_type, UsableAsKey>(
table_.alloc(), table_.alloc(),
[&](auto&&... inner) { [&](auto&&... inner) {
return table_.tryEmplaceValue( return table_.tryEmplaceValue(
......
...@@ -84,10 +84,26 @@ struct TemporaryEmplaceKey { ...@@ -84,10 +84,26 @@ struct TemporaryEmplaceKey {
// about heterogeneous lookup you can just pass a single-arg template // about heterogeneous lookup you can just pass a single-arg template
// that extends std::false_type. // that extends std::false_type.
template <typename Func, typename KeyType, typename Arg1, typename Arg2> // TODO(T31574848): We can remove the std::enable_if_t once we no longer
// target platforms without N4387 ("perfect initialization" for pairs
// and tuples). libstdc++ at gcc-6.1.0 is the first release that contains
// the improved set of pair constructors.
template <
typename KeyType,
typename MappedType,
typename Func,
typename UsableKeyType,
typename Arg1,
typename Arg2,
std::enable_if_t<
std::is_constructible<
std::pair<KeyType const, MappedType>,
Arg1&&,
Arg2&&>::value,
int> = 0>
auto callWithKeyAndPairArgs( auto callWithKeyAndPairArgs(
Func&& f, Func&& f,
KeyType const& key, UsableKeyType const& key,
std::tuple<Arg1>&& first_args, std::tuple<Arg1>&& first_args,
std::tuple<Arg2>&& second_args) { std::tuple<Arg2>&& second_args) {
return f( return f(
...@@ -96,10 +112,16 @@ auto callWithKeyAndPairArgs( ...@@ -96,10 +112,16 @@ auto callWithKeyAndPairArgs(
std::forward<Arg2>(std::get<0>(second_args))); std::forward<Arg2>(std::get<0>(second_args)));
} }
template <typename Func, typename KeyType, typename... Args1, typename... Args2> template <
typename KeyType,
typename MappedType,
typename Func,
typename UsableKeyType,
typename... Args1,
typename... Args2>
auto callWithKeyAndPairArgs( auto callWithKeyAndPairArgs(
Func&& f, Func&& f,
KeyType const& key, UsableKeyType const& key,
std::tuple<Args1...>&& first_args, std::tuple<Args1...>&& first_args,
std::tuple<Args2...>&& second_args) { std::tuple<Args2...>&& second_args) {
return f( return f(
...@@ -109,9 +131,13 @@ auto callWithKeyAndPairArgs( ...@@ -109,9 +131,13 @@ auto callWithKeyAndPairArgs(
std::move(second_args)); std::move(second_args));
} }
template <typename>
using ExactKeyMatchOnly = std::false_type;
template < template <
typename KeyType, typename KeyType,
template <typename> class UsableAsKey, typename MappedType,
template <typename> class UsableAsKey = ExactKeyMatchOnly,
typename Alloc, typename Alloc,
typename Func, typename Func,
typename Arg1, typename Arg1,
...@@ -128,7 +154,7 @@ auto callWithExtractedKey( ...@@ -128,7 +154,7 @@ auto callWithExtractedKey(
std::tuple<Args2...>&& second_args) { std::tuple<Args2...>&& second_args) {
// we found a usable key in the args :) // we found a usable key in the args :)
auto const& key = std::get<0>(first_args); auto const& key = std::get<0>(first_args);
return callWithKeyAndPairArgs( return callWithKeyAndPairArgs<KeyType, MappedType>(
std::forward<Func>(f), std::forward<Func>(f),
key, key,
std::tuple<Arg1&&>(std::move(first_args)), std::tuple<Arg1&&>(std::move(first_args)),
...@@ -137,7 +163,8 @@ auto callWithExtractedKey( ...@@ -137,7 +163,8 @@ auto callWithExtractedKey(
template < template <
typename KeyType, typename KeyType,
template <typename> class UsableAsKey, typename MappedType,
template <typename> class UsableAsKey = ExactKeyMatchOnly,
typename Alloc, typename Alloc,
typename Func, typename Func,
typename... Args1, typename... Args1,
...@@ -151,7 +178,7 @@ auto callWithExtractedKey( ...@@ -151,7 +178,7 @@ auto callWithExtractedKey(
// we will need to materialize a temporary key :( // we will need to materialize a temporary key :(
TemporaryEmplaceKey<KeyType, Alloc> key( TemporaryEmplaceKey<KeyType, Alloc> key(
a, std::tuple<Args1&&...>(std::move(first_args))); a, std::tuple<Args1&&...>(std::move(first_args)));
return callWithKeyAndPairArgs( return callWithKeyAndPairArgs<KeyType, MappedType>(
std::forward<Func>(f), std::forward<Func>(f),
const_cast<KeyType const&>(key.value()), const_cast<KeyType const&>(key.value()),
std::forward_as_tuple(std::move(key.value())), std::forward_as_tuple(std::move(key.value())),
...@@ -160,11 +187,12 @@ auto callWithExtractedKey( ...@@ -160,11 +187,12 @@ auto callWithExtractedKey(
template < template <
typename KeyType, typename KeyType,
template <typename> class UsableAsKey, typename MappedType,
template <typename> class UsableAsKey = ExactKeyMatchOnly,
typename Alloc, typename Alloc,
typename Func> typename Func>
auto callWithExtractedKey(Alloc& a, Func&& f) { auto callWithExtractedKey(Alloc& a, Func&& f) {
return callWithExtractedKey<KeyType, UsableAsKey>( return callWithExtractedKey<KeyType, MappedType, UsableAsKey>(
a, a,
std::forward<Func>(f), std::forward<Func>(f),
std::piecewise_construct, std::piecewise_construct,
...@@ -174,13 +202,14 @@ auto callWithExtractedKey(Alloc& a, Func&& f) { ...@@ -174,13 +202,14 @@ auto callWithExtractedKey(Alloc& a, Func&& f) {
template < template <
typename KeyType, typename KeyType,
template <typename> class UsableAsKey, typename MappedType,
template <typename> class UsableAsKey = ExactKeyMatchOnly,
typename Alloc, typename Alloc,
typename Func, typename Func,
typename U1, typename U1,
typename U2> typename U2>
auto callWithExtractedKey(Alloc& a, Func&& f, U1&& x, U2&& y) { auto callWithExtractedKey(Alloc& a, Func&& f, U1&& x, U2&& y) {
return callWithExtractedKey<KeyType, UsableAsKey>( return callWithExtractedKey<KeyType, MappedType, UsableAsKey>(
a, a,
std::forward<Func>(f), std::forward<Func>(f),
std::piecewise_construct, std::piecewise_construct,
...@@ -190,13 +219,14 @@ auto callWithExtractedKey(Alloc& a, Func&& f, U1&& x, U2&& y) { ...@@ -190,13 +219,14 @@ auto callWithExtractedKey(Alloc& a, Func&& f, U1&& x, U2&& y) {
template < template <
typename KeyType, typename KeyType,
template <typename> class UsableAsKey, typename MappedType,
template <typename> class UsableAsKey = ExactKeyMatchOnly,
typename Alloc, typename Alloc,
typename Func, typename Func,
typename U1, typename U1,
typename U2> typename U2>
auto callWithExtractedKey(Alloc& a, Func&& f, std::pair<U1, U2> const& p) { auto callWithExtractedKey(Alloc& a, Func&& f, std::pair<U1, U2> const& p) {
return callWithExtractedKey<KeyType, UsableAsKey>( return callWithExtractedKey<KeyType, MappedType, UsableAsKey>(
a, a,
std::forward<Func>(f), std::forward<Func>(f),
std::piecewise_construct, std::piecewise_construct,
...@@ -206,7 +236,8 @@ auto callWithExtractedKey(Alloc& a, Func&& f, std::pair<U1, U2> const& p) { ...@@ -206,7 +236,8 @@ auto callWithExtractedKey(Alloc& a, Func&& f, std::pair<U1, U2> const& p) {
template < template <
typename KeyType, typename KeyType,
template <typename> class UsableAsKey, typename MappedType,
template <typename> class UsableAsKey = ExactKeyMatchOnly,
typename Alloc, typename Alloc,
typename Func, typename Func,
typename U1, typename U1,
...@@ -215,7 +246,7 @@ auto callWithExtractedKey(Alloc& a, Func&& f, std::pair<U1, U2>&& p) { ...@@ -215,7 +246,7 @@ auto callWithExtractedKey(Alloc& a, Func&& f, std::pair<U1, U2>&& p) {
// std::move(p.first) is wrong because if U1 is an lvalue reference the // std::move(p.first) is wrong because if U1 is an lvalue reference the
// result will incorrectly be an rvalue ref. static_cast here allows // result will incorrectly be an rvalue ref. static_cast here allows
// proper ref collapsing // proper ref collapsing
return callWithExtractedKey<KeyType, UsableAsKey>( return callWithExtractedKey<KeyType, MappedType, UsableAsKey>(
a, a,
std::forward<Func>(f), std::forward<Func>(f),
std::piecewise_construct, std::piecewise_construct,
...@@ -228,7 +259,7 @@ auto callWithExtractedKey(Alloc& a, Func&& f, std::pair<U1, U2>&& p) { ...@@ -228,7 +259,7 @@ auto callWithExtractedKey(Alloc& a, Func&& f, std::pair<U1, U2>&& p) {
template < template <
typename KeyType, typename KeyType,
template <typename> class UsableAsKey, template <typename> class UsableAsKey = ExactKeyMatchOnly,
typename Alloc, typename Alloc,
typename Func, typename Func,
typename Arg, typename Arg,
...@@ -244,7 +275,7 @@ auto callWithConstructedKey(Alloc&, Func&& f, Arg&& arg) { ...@@ -244,7 +275,7 @@ auto callWithConstructedKey(Alloc&, Func&& f, Arg&& arg) {
template < template <
typename KeyType, typename KeyType,
template <typename> class UsableAsKey, template <typename> class UsableAsKey = ExactKeyMatchOnly,
typename Alloc, typename Alloc,
typename Func, typename Func,
typename... Args> typename... Args>
......
...@@ -301,14 +301,11 @@ void runKeyExtractCases( ...@@ -301,14 +301,11 @@ void runKeyExtractCases(
} }
} }
template <typename T>
using FalseFunc1 = std::false_type;
struct DoEmplace1 { struct DoEmplace1 {
template <typename F, typename P> template <typename F, typename P>
void operator()(F&& f, P&& p) const { void operator()(F&& f, P&& p) const {
std::allocator<char> a; std::allocator<char> a;
detail::callWithExtractedKey<Tracked<0>, FalseFunc1>( detail::callWithExtractedKey<Tracked<0>, Tracked<1>>(
a, std::forward<F>(f), std::forward<P>(p)); a, std::forward<F>(f), std::forward<P>(p));
} }
}; };
...@@ -317,14 +314,14 @@ struct DoEmplace2 { ...@@ -317,14 +314,14 @@ struct DoEmplace2 {
template <typename F, typename U1, typename U2> template <typename F, typename U1, typename U2>
void operator()(F&& f, std::pair<U1, U2> const& p) const { void operator()(F&& f, std::pair<U1, U2> const& p) const {
std::allocator<char> a; std::allocator<char> a;
detail::callWithExtractedKey<Tracked<0>, FalseFunc1>( detail::callWithExtractedKey<Tracked<0>, Tracked<1>>(
a, std::forward<F>(f), p.first, p.second); a, std::forward<F>(f), p.first, p.second);
} }
template <typename F, typename U1, typename U2> template <typename F, typename U1, typename U2>
void operator()(F&& f, std::pair<U1, U2>&& p) const { void operator()(F&& f, std::pair<U1, U2>&& p) const {
std::allocator<char> a; std::allocator<char> a;
detail::callWithExtractedKey<Tracked<0>, FalseFunc1>( detail::callWithExtractedKey<Tracked<0>, Tracked<1>>(
a, std::forward<F>(f), std::move(p.first), std::move(p.second)); a, std::forward<F>(f), std::move(p.first), std::move(p.second));
} }
}; };
...@@ -333,7 +330,7 @@ struct DoEmplace3 { ...@@ -333,7 +330,7 @@ struct DoEmplace3 {
template <typename F, typename U1, typename U2> template <typename F, typename U1, typename U2>
void operator()(F&& f, std::pair<U1, U2> const& p) const { void operator()(F&& f, std::pair<U1, U2> const& p) const {
std::allocator<char> a; std::allocator<char> a;
detail::callWithExtractedKey<Tracked<0>, FalseFunc1>( detail::callWithExtractedKey<Tracked<0>, Tracked<1>>(
a, a,
std::forward<F>(f), std::forward<F>(f),
std::piecewise_construct, std::piecewise_construct,
...@@ -344,7 +341,7 @@ struct DoEmplace3 { ...@@ -344,7 +341,7 @@ struct DoEmplace3 {
template <typename F, typename U1, typename U2> template <typename F, typename U1, typename U2>
void operator()(F&& f, std::pair<U1, U2>&& p) const { void operator()(F&& f, std::pair<U1, U2>&& p) const {
std::allocator<char> a; std::allocator<char> a;
detail::callWithExtractedKey<Tracked<0>, FalseFunc1>( detail::callWithExtractedKey<Tracked<0>, Tracked<1>>(
a, a,
std::forward<F>(f), std::forward<F>(f),
std::piecewise_construct, std::piecewise_construct,
...@@ -361,7 +358,7 @@ struct DoEmplace3Value { ...@@ -361,7 +358,7 @@ struct DoEmplace3Value {
template <typename F, typename U1, typename U2> template <typename F, typename U1, typename U2>
void operator()(F&& f, std::pair<U1, U2> const& p) const { void operator()(F&& f, std::pair<U1, U2> const& p) const {
std::allocator<char> a; std::allocator<char> a;
detail::callWithExtractedKey<Tracked<0>, FalseFunc1>( detail::callWithExtractedKey<Tracked<0>, Tracked<1>>(
a, a,
std::forward<F>(f), std::forward<F>(f),
std::piecewise_construct, std::piecewise_construct,
...@@ -372,7 +369,7 @@ struct DoEmplace3Value { ...@@ -372,7 +369,7 @@ struct DoEmplace3Value {
template <typename F, typename U1, typename U2> template <typename F, typename U1, typename U2>
void operator()(F&& f, std::pair<U1, U2>&& p) const { void operator()(F&& f, std::pair<U1, U2>&& p) const {
std::allocator<char> a; std::allocator<char> a;
detail::callWithExtractedKey<Tracked<0>, FalseFunc1>( detail::callWithExtractedKey<Tracked<0>, Tracked<1>>(
a, a,
std::forward<F>(f), std::forward<F>(f),
std::piecewise_construct, std::piecewise_construct,
...@@ -390,7 +387,7 @@ TEST(Util, callWithExtractedKey) { ...@@ -390,7 +387,7 @@ TEST(Util, callWithExtractedKey) {
// Calling the default pair constructor via emplace is valid, but not // Calling the default pair constructor via emplace is valid, but not
// very useful in real life. Verify that it works. // very useful in real life. Verify that it works.
std::allocator<char> a; std::allocator<char> a;
detail::callWithExtractedKey<Tracked<0>, FalseFunc1>( detail::callWithExtractedKey<Tracked<0>, Tracked<1>>(
a, [](Tracked<0> const& key, auto&&... args) { a, [](Tracked<0> const& key, auto&&... args) {
EXPECT_TRUE(key == 0); EXPECT_TRUE(key == 0);
std::pair<Tracked<0> const, Tracked<1>> p( std::pair<Tracked<0> const, Tracked<1>> p(
...@@ -417,23 +414,23 @@ TEST(Util, callWithConstructedKey) { ...@@ -417,23 +414,23 @@ TEST(Util, callWithConstructedKey) {
uint64_t k3 = 0; uint64_t k3 = 0;
sink.reset(); sink.reset();
resetTracking(); resetTracking();
detail::callWithConstructedKey<Tracked<0>, FalseFunc1>(a, sinkFunc, k1); detail::callWithConstructedKey<Tracked<0>>(a, sinkFunc, k1);
// copy is expected on successful emplace // copy is expected on successful emplace
EXPECT_EQ(Tracked<0>::counts().dist(Counts{1, 0, 0, 0}), 0); EXPECT_EQ(Tracked<0>::counts().dist(Counts{1, 0, 0, 0}), 0);
resetTracking(); resetTracking();
detail::callWithConstructedKey<Tracked<0>, FalseFunc1>(a, sinkFunc, k2); detail::callWithConstructedKey<Tracked<0>>(a, sinkFunc, k2);
// no copies or moves on failing emplace with value_type // no copies or moves on failing emplace with value_type
EXPECT_EQ(Tracked<0>::counts().dist(Counts{0, 0, 0, 0}), 0); EXPECT_EQ(Tracked<0>::counts().dist(Counts{0, 0, 0, 0}), 0);
resetTracking(); resetTracking();
detail::callWithConstructedKey<Tracked<0>, FalseFunc1>(a, sinkFunc, k3); detail::callWithConstructedKey<Tracked<0>>(a, sinkFunc, k3);
// copy convert expected for failing emplace with wrong type // copy convert expected for failing emplace with wrong type
EXPECT_EQ(Tracked<0>::counts().dist(Counts{0, 0, 1, 0}), 0); EXPECT_EQ(Tracked<0>::counts().dist(Counts{0, 0, 1, 0}), 0);
sink.reset(); sink.reset();
resetTracking(); resetTracking();
detail::callWithConstructedKey<Tracked<0>, FalseFunc1>(a, sinkFunc, k3); detail::callWithConstructedKey<Tracked<0>>(a, sinkFunc, k3);
// copy convert + move expected for successful emplace with wrong type // copy convert + move expected for successful emplace with wrong type
EXPECT_EQ(Tracked<0>::counts().dist(Counts{0, 1, 1, 0}), 0); EXPECT_EQ(Tracked<0>::counts().dist(Counts{0, 1, 1, 0}), 0);
} }
...@@ -443,27 +440,23 @@ TEST(Util, callWithConstructedKey) { ...@@ -443,27 +440,23 @@ TEST(Util, callWithConstructedKey) {
uint64_t k3 = 0; uint64_t k3 = 0;
sink.reset(); sink.reset();
resetTracking(); resetTracking();
detail::callWithConstructedKey<Tracked<0>, FalseFunc1>( detail::callWithConstructedKey<Tracked<0>>(a, sinkFunc, std::move(k1));
a, sinkFunc, std::move(k1));
// move is expected on successful emplace // move is expected on successful emplace
EXPECT_EQ(Tracked<0>::counts().dist(Counts{0, 1, 0, 0}), 0); EXPECT_EQ(Tracked<0>::counts().dist(Counts{0, 1, 0, 0}), 0);
resetTracking(); resetTracking();
detail::callWithConstructedKey<Tracked<0>, FalseFunc1>( detail::callWithConstructedKey<Tracked<0>>(a, sinkFunc, std::move(k2));
a, sinkFunc, std::move(k2));
// no copies or moves on failing emplace with value_type // no copies or moves on failing emplace with value_type
EXPECT_EQ(Tracked<0>::counts().dist(Counts{0, 0, 0, 0}), 0); EXPECT_EQ(Tracked<0>::counts().dist(Counts{0, 0, 0, 0}), 0);
resetTracking(); resetTracking();
detail::callWithConstructedKey<Tracked<0>, FalseFunc1>( detail::callWithConstructedKey<Tracked<0>>(a, sinkFunc, std::move(k3));
a, sinkFunc, std::move(k3));
// move convert expected for failing emplace with wrong type // move convert expected for failing emplace with wrong type
EXPECT_EQ(Tracked<0>::counts().dist(Counts{0, 0, 0, 1}), 0); EXPECT_EQ(Tracked<0>::counts().dist(Counts{0, 0, 0, 1}), 0);
sink.reset(); sink.reset();
resetTracking(); resetTracking();
detail::callWithConstructedKey<Tracked<0>, FalseFunc1>( detail::callWithConstructedKey<Tracked<0>>(a, sinkFunc, std::move(k3));
a, sinkFunc, std::move(k3));
// move convert + move expected for successful emplace with wrong type // move convert + move expected for successful emplace with wrong type
EXPECT_EQ(Tracked<0>::counts().dist(Counts{0, 1, 0, 1}), 0); EXPECT_EQ(Tracked<0>::counts().dist(Counts{0, 1, 0, 1}), 0);
} }
...@@ -471,7 +464,7 @@ TEST(Util, callWithConstructedKey) { ...@@ -471,7 +464,7 @@ TEST(Util, callWithConstructedKey) {
// Calling the default pair constructor via emplace is valid, but not // Calling the default pair constructor via emplace is valid, but not
// very useful in real life. Verify that it works. // very useful in real life. Verify that it works.
sink.reset(); sink.reset();
detail::callWithConstructedKey<Tracked<0>, FalseFunc1>(a, sinkFunc); detail::callWithConstructedKey<Tracked<0>>(a, sinkFunc);
EXPECT_TRUE(sink.has_value()); EXPECT_TRUE(sink.has_value());
} }
...@@ -516,51 +509,51 @@ TEST(Util, callWithExtractedHeterogeneousKey) { ...@@ -516,51 +509,51 @@ TEST(Util, callWithExtractedHeterogeneousKey) {
// none of the std::move below actually get consumed // none of the std::move below actually get consumed
detail::callWithExtractedKey<std::string, IsStringPiece>( detail::callWithExtractedKey<std::string, int, IsStringPiece>(
a, ExpectArgTypes<std::string, std::string&, int&&>{0, &str}, str, 0); a, ExpectArgTypes<std::string, std::string&, int&&>{0, &str}, str, 0);
detail::callWithExtractedKey<std::string, IsStringPiece>( detail::callWithExtractedKey<std::string, int, IsStringPiece>(
a, a,
ExpectArgTypes<std::string, std::string const&, int const&>{ ExpectArgTypes<std::string, std::string const&, int const&>{
1, &strPair.first}, 1, &strPair.first},
strPair); strPair);
detail::callWithExtractedKey<std::string, IsStringPiece>( detail::callWithExtractedKey<std::string, int, IsStringPiece>(
a, a,
ExpectArgTypes<std::string, std::string&, int&&>{2, &str}, ExpectArgTypes<std::string, std::string&, int&&>{2, &str},
std::move(strLRefPair)); std::move(strLRefPair));
detail::callWithExtractedKey<std::string, IsStringPiece>( detail::callWithExtractedKey<std::string, int, IsStringPiece>(
a, a,
ExpectArgTypes<std::string, std::string&, int const&>{10, &str}, ExpectArgTypes<std::string, std::string&, int const&>{10, &str},
strLRefPair); strLRefPair);
detail::callWithExtractedKey<std::string, IsStringPiece>( detail::callWithExtractedKey<std::string, int, IsStringPiece>(
a, a,
ExpectArgTypes<std::string, std::string&&, int&&>{3, &str}, ExpectArgTypes<std::string, std::string&&, int&&>{3, &str},
std::move(str), std::move(str),
0); 0);
detail::callWithExtractedKey<std::string, IsStringPiece>( detail::callWithExtractedKey<std::string, int, IsStringPiece>(
a, a,
ExpectArgTypes<std::string, std::string&&, int&&>{4, &strPair.first}, ExpectArgTypes<std::string, std::string&&, int&&>{4, &strPair.first},
std::move(strPair)); std::move(strPair));
detail::callWithExtractedKey<std::string, IsStringPiece>( detail::callWithExtractedKey<std::string, int, IsStringPiece>(
a, a,
ExpectArgTypes<std::string, std::string&, int const&>{5, &str}, ExpectArgTypes<std::string, std::string&, int const&>{5, &str},
strRRefPair); strRRefPair);
detail::callWithExtractedKey<std::string, IsStringPiece>( detail::callWithExtractedKey<std::string, int, IsStringPiece>(
a, a,
ExpectArgTypes<std::string, std::string&&, int&&>{11, &str}, ExpectArgTypes<std::string, std::string&&, int&&>{11, &str},
std::move(strRRefPair)); std::move(strRRefPair));
detail::callWithExtractedKey<std::string, IsStringPiece>( detail::callWithExtractedKey<std::string, int, IsStringPiece>(
a, ExpectArgTypes<StringPiece, StringPiece&, int&&>{6, &sp}, sp, 0); a, ExpectArgTypes<StringPiece, StringPiece&, int&&>{6, &sp}, sp, 0);
detail::callWithExtractedKey<std::string, IsStringPiece>( detail::callWithExtractedKey<std::string, int, IsStringPiece>(
a, a,
ExpectArgTypes<StringPiece, StringPiece const&, int const&>{ ExpectArgTypes<StringPiece, StringPiece const&, int const&>{
7, &spPair.first}, 7, &spPair.first},
spPair); spPair);
detail::callWithExtractedKey<std::string, IsStringPiece>( detail::callWithExtractedKey<std::string, int, IsStringPiece>(
a, ExpectArgTypes<std::string, std::string&&, int&&>{8}, ptr, 0); a, ExpectArgTypes<std::string, std::string&&, int&&>{8}, ptr, 0);
detail::callWithExtractedKey<std::string, IsStringPiece>( detail::callWithExtractedKey<std::string, int, IsStringPiece>(
a, ExpectArgTypes<std::string, std::string&&, int const&>{9}, ptrPair); a, ExpectArgTypes<std::string, std::string&&, int const&>{9}, ptrPair);
} }
......
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