-
-
Save jwpeterson/ed14bcb668b89ff8268db493e75d853a to your computer and use it in GitHub Desktop.
// Compilation requires -std=c++17 | |
// http://stackoverflow.com/questions/17603666/copy-move-requirements-for-the-key-value-types-in-a-stdmap | |
#include <map> | |
#include <vector> | |
struct foo | |
{ | |
int i; | |
foo(int j) : i(j) {} | |
// make foo non-copyable | |
foo(const foo &) = delete; // copy ctor | |
foo & operator=(const foo &) = delete; // assignment op | |
// Use default move ctors. You have to declare these, otherwise the | |
// class will not have automatically generated move ctors. | |
// foo (foo && other) = default; | |
foo & operator=(foo &&) = default; | |
// Non-default version of move ctor. Use noexcept to maintain the | |
// "strong exception guarantee". This is especially important for | |
// classes which are to be used in std containers -- the container | |
// may instead use the copy constructor (assuming it's not deleted) | |
// if the move constructor is not marked noexcept! Neither GCC nor | |
// clang seem to care whether noexcept is used in this case... | |
foo (foo && other) noexcept : | |
i(std::move(other.i)) | |
{} | |
}; | |
// Required for foo to be used as a key in std::map | |
bool operator<(const foo & f1, const foo & f2) | |
{ | |
return f1.i < f2.i; | |
} | |
int main(int argc, char ** argv) | |
{ | |
// Test whether we can create a map where keys are non-copyable. We | |
// can, but the map itself cannot be copied (makes sense). | |
{ | |
std::map<foo, int> f; | |
// std::map<foo, int> f2 = f; // compile error - can't copy map with uncopyable items | |
std::map<foo, int> f3 = std::move(f); // OK: when moving, nothing is copied | |
} | |
// Test whether we can create a map where values are non-copyable. | |
{ | |
std::map<int, foo> f; | |
std::map<int, foo> f3 = std::move(f); // OK: when moving, nothing is copied | |
} | |
// Test inserting noncopyable keys into the map. | |
{ | |
std::map<foo, int> f; | |
f.insert(std::make_pair(foo(1), 1)); // works if foo has move ctor | |
f[foo(1)] = 1; // works if foo has move ctor | |
f.emplace(std::make_pair(foo(1), 1)); // works if foo has move ctor | |
f.emplace(foo(1), 1); // works if foo has move ctor | |
f.emplace_hint(/*hint=*/f.begin(), foo(1), 1); | |
f.try_emplace(foo(1), 1); | |
f.try_emplace(/*hint=*/f.begin(), foo(1), 1); | |
} | |
// Test non-copyable items in std::vector. | |
{ | |
std::vector<foo> f; | |
f.push_back(foo(1)); // works if foo has move ctor | |
f.emplace_back(foo(1)); // works if foo has move ctor | |
f.emplace_back(1); // works by forwarding the arg to the foo ctor | |
} | |
return 0; | |
} |
In the gist I did not test calling try_emplace
for a non-copyable value_type. Does your code work if you change it to call regular emplace
instead? You may also want to try with more than one compiler, just to see if you get a different result.
I figured out the problem. The Type1
and Type2
are provided by a header file. Like API 1's type is Type1
and API 2's type is Type2
etc. I opened the header file to look and these types are type aliases / template types. Unfortunately some of the base types for these types are same, out of many. For example
using Type1 = uint64_t;
using Type2 = uint64_t;
This was confusing the std::map::try_emplace
with their template condition __exactly_once
. So, neither did the std::in_place_type_t
based constructor (6th in https://en.cppreference.com/w/cpp/utility/variant/variant) work. The final solution is to use std::in_place_index_t
based constructor and use the specific index to emplace.
Hi Mr Peterson,
I'm using a map of type
std::map<long, std::variant<fu2::unique_function<Type1>, fu2::unique_function<Type2>...>>
and I can't insert into it. The line below throws errorError:
Any idea here?
Fu2: Unique function that disables copy constructor. And its only movable. https://github.com/Naios/function2