-
-
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