Last active
November 2, 2024 05:52
-
-
Save jwpeterson/ed14bcb668b89ff8268db493e75d853a to your computer and use it in GitHub Desktop.
Demonstrate usage of noncopyable class in std containers
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I figured out the problem. The
Type1
andType2
are provided by a header file. Like API 1's type isType1
and API 2's type isType2
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 exampleThis was confusing the
std::map::try_emplace
with their template condition__exactly_once
. So, neither did thestd::in_place_type_t
based constructor (6th in https://en.cppreference.com/w/cpp/utility/variant/variant) work. The final solution is to usestd::in_place_index_t
based constructor and use the specific index to emplace.