std::unique_ptr custom deleters seem to need a pointer argument -- is there an alternative that can take a non-pointer argument? - c++

I was recently introduced to this mechanism of using a std::unique_ptr to implement a "generic" RAII mechanism:
// main.cpp
#include <memory>
#include <sys/fcntl.h>
#include <unistd.h>
#define RAII_CAT(x) raii ## x
#define RAII_CAT2(x) RAII_CAT(x)
#define RAII_FD(X) \
std::unique_ptr<int, void(*)(int*)> RAII_CAT2(__LINE__){X, [](int* x){ if (-1 != *x) { close(*x); }}}
int main(int argc, char* argv[]) {
{
int fd = open("foo.txt", O_RDONLY);
RAII_FD(&fd);
}
end:
return 0;
}
In the above code, the RAII_FD macro creates a std::unique_ptr object whose custom deleter takes an int* -- the pointer to a file-descriptor -- and calls close() on the file-descriptor.
I like this mechanism a lot, but I have a minor gripe that the custom deleter requires a pointer as its argument: aesthetically it feels a bit less than desirable.
E.g. in the code above, the custom deleter is a thin wrapper over int close(int) -- it would be nice, therefore, if the custom deleter could take an int instead of an int*...in which case perhaps a wrapper function wouldn't be necessary at all: perhaps a function pointer to int close(int) itself could be supplied.
I.e. variations of the following were tried, trying to register a custom deleter with signature void func(int) instead of void func(int*):
// main.cpp
#include <memory>
#include <sys/fcntl.h>
#include <unistd.h>
#define RAII_CAT(x) raii ## x
#define RAII_CAT2(x) RAII_CAT(x)
#define RAII_FD(X) \
std::unique_ptr<int, void(*)(int)> RAII_CAT2(__LINE__){X, [](int x){ if (-1 != x) { close(x); }}}
int main(int argc, char* argv[]) {
{
int fd = open("foo.txt", O_RDONLY);
RAII_FD(fd);
}
end:
return 0;
}
...the compile error was a vomit of stl errors that I perhaps don't grok 100%, but I think the gist is there are int*/int mismatches in the various template expansions.
Is there another similar mechanism by which one can implement a "generic" RAII mechanism with custom deleters whose argument doesn't necessarily need to be a pointer?
I'm open to learning about all possible solutions, but solutions that are actually usable in my target environment must be C++11 and non-Boost. Most preferable would be some similar "thin wrapper" over STL-native objects.

std::unique_ptr doesn't in fact operate only on native raw pointers. It supports all types satisfying the NullablePointer requirements. Basically the type is supposed to behave like a scalar type with respect to value semantics and equality comparison and be nullable with nullptr into a distinct null state that is also the value-initialized state. This requires that you have a state to represent the null state, which in your case can be served by -1.
Such a type would be provided to unique_ptr as a type member pointer of the custom deleter:
// Code not tested!
// You must check that I didn't make a stupid mistake or forgot a requirement!
struct Deleter {
struct pointer {
int fd = -1;
pointer() noexcept = default;
pointer(std::nullptr_t) noexcept {}
// should probably be explicit
// but then `fd` cannot be passed directly to the
// std::unique_ptr constructor
pointer(int fd) noexcept : fd(fd) {};
friend bool operator==(pointer p, pointer q) noexcept {
return p.fd == q.fd;
}
// operator!= is optional with C++20
friend bool operator!=(pointer p, pointer q) noexcept {
return !(p == q);
}
operator bool() noexcept { return *this != nullptr; }
};
// unique_ptr calls this only if pointer doesn't represent the null state
void operator()(pointer x) noexcept { close(x.fd); }
};
Then you can use
std::unique_ptr<int, Deleter> u(fd);
in the way you intent it to. In fact it doesn't really matter that the lement type is int. std::unique_ptr<double, Deleter> would work as well. However, you can't actually dereference with *u. If that is required, then you need to give pointer a operator* as well and in that case the element type should match the type returned by it.
Whether this is really useful given the amount of boilerplate code and the somewhat unusual interpretation of pointer is up to you.

Related

How to move a unique_ptr without custom deleter to another unique_ptr with custom deleter?

#include <memory>
#include <functional>
#include <iostream>
struct TestStruct {
int a = 100;
};
struct StructDeleter {
void operator()(TestStruct *ptr) const {
delete ptr;
}
};
std::unique_ptr<TestStruct, StructDeleter> MakeNewStruct() {
std::unique_ptr<TestStruct> st(new TestStruct());
std::unique_ptr<TestStruct, StructDeleter> customDeleterSt(std::move(st));
std::cout << customDeleterSt->a << std::endl;
return customDeleterSt;
}
int main() {
auto a = MakeNewStruct();
std::cout << a->a << std::endl;
return 0;
}
The above code can not be compiled, how to move st to customDeleterSt?
I get a st unique_ptr from the st creation interface, and I use the custom deleter to hide the implementation of st from my users, so how to move a unique_ptr without custom deleter to a unique_tr with custom deleter?
Thanks for any help!
As noted in the comments, the brute-force way is to have the source .release() to the constructor of the destination. However there is a much more elegant solution (imho):
Add an implicit conversion from std::default_delete<TestStruct> to StructDeleter:
struct StructDeleter {
StructDeleter(std::default_delete<TestStruct>) {} // Add this
void operator()(TestStruct *ptr) const {
delete ptr;
}
};
Now the code works with the existing move construction syntax.
Whether this is done via the converting constructor, or .release(), if StructDeleter can't (for whatever reasons) properly delete a pointer that std::default_delete handles, this will result in undefined behavior. It is only because StructDeleter calls delete ptr that either of these techniques works.
As written, TestStruct does not need to be a complete type at the point of delete. However should TestStruct acquire a non-trivial destructor, you will also need to ensure that for any code that calls StructDeleter::operator()(TestStruct*), that TestStruct is a complete type, otherwise you're back into UB territory again. This assurance is one of the things that std::default_delete<TestStruct> does for you and that StructDeleter (as written) does not.
If it is intended to keep ~TestStruct() trivial, it would be a good idea to add:
static_assert(std::is_trivially_destructible<TestStruct>::value);

C++ Get reference of object inside vector using function

Can you get a reference to an object that is within a vector through a function? I could do this with pointers easily, but you know, we're all obsessed here with "Don't use pointers".
This is a simple example. The absolute limitation is it must be done from a function call (so that function call can return false if not found).
// Example program
#include <iostream>
#include <string>
#include <vector>
class Dev {
public:
Dev(){}
std::string name;
};
void geter(std::vector<Dev> &devs, Dev &a){
a = devs.at(0);
}
int main()
{
Dev d;
d.name = "original name";
std::vector<Dev> devs;
devs.push_back(d);
Dev a;
geter(devs, a);
a.name = "new name";
std::cout << d.name; // still prints "original name"
}
Can you get a reference to an object that is within a vector through a function?
Yes, but you cannot return that reference via a function parameter. A reference can be bound to an element only at the point of initializing the reference. Once you are inside a function, it is too late to initialize the function's parameters, too late to bind a reference. You can return a reference via a function's return value, but not via an output parameter.
References also fail to cover the "not found" possibility, as a reference must be bound to something.
The language feature that allows the functionality you are looking for is called a "pointer".
References are useful if they can be bound during initialization, never need to change what they are bound to, and never need to be in a state of not being bound. The first parameter to your geter function is an example of this.
Pointers are useful if they need to point to different objects during their lifetime, or if they might need to be in an "unbound" state (a.k.a. be null). Think of a pointer as a reference that can be reseated (refer to a different object than it did before) and that can be unseated (refer to no object). The intended functionality of the second parameter to your geter function is an example of this.
I could do this with pointers easily,
Good. You know the right tool for the job. Do it.
but you know, we're all obsessed here with "Don't use pointers".
No, I do not know that. In fact, that is bad advice when stated that broadly. Pointers still have their place in modern C++. The "obsession" you probably are referring to is "don't use owning pointers". That is, don't use a pointer if you have to remember to delete the thing to which the pointer points. If there is no ownership involved (i.e. no responsibility for freeing memory), then there is nothing inherently bad about using pointers. In fact, pointers are often a more appropriate choice than references when "does not exist" is a valid possibility (just remember to check for null, which your logic would call for anyway).
Note: There are other "obsessions" that fall under "don't use pointers", but I don't see another that is relevant here. For the sake of an example: there is also "don't use a pointer when a reference will get the job done." This is good advice, but in this case a reference will not get the job done.
Nah. Doing it via parameters is silly. It's the year 2020 and you have std::optional and such.
I'd do it as follows. First, some helper code:
#include <type_traits>
#include <vector>
template <typename T> using const_qualified_value_type_impl =
std::conditional_t<std::is_const_v<T>,
typename std::add_const_t<typename T::value_type>,
typename T::value_type>;
template <typename T> using const_qualified_value_type =
const_qualified_value_type_impl<std::remove_reference_t<T>>;
static_assert(std::is_same_v<const_qualified_value_type<std::vector<int>>, int>, "");
static_assert(std::is_same_v<const_qualified_value_type<const std::vector<int>>, const int>, "");
#include <functional>
#include <optional>
template <typename T> class optional_ref
{
std::optional<std::reference_wrapper<T>> val;
public:
template <typename ...Args> constexpr optional_ref(Args &&...args) :
val(std::forward<Args>(args)...) {}
constexpr explicit operator bool() const { return static_cast<bool>(val); }
constexpr auto has_value() const { return val.has_value(); }
constexpr auto &value() const { return val.value().get(); }
constexpr auto &get() const { return val.value().get(); }
constexpr operator T&() const { return val.value().get(); }
};
Now we have an optional_ref type (a very rudimentary one, but still),
and we can use it when creating the get_first getter:
template <typename C>
auto get_first(C && container) -> optional_ref<const_qualified_value_type<C>>
{
auto const begin = container.begin();
static_assert(std::is_reference_v<decltype(*begin)>, "*begin() must return a reference");
if (begin == container.end()) return {};
return *begin;
}
Now a basic test:
#include <cassert>
int main()
{
std::vector<int> vect;
assert(!get_first(vect));
vect.push_back(0);
assert(get_first(vect));
assert(get_first(vect) == 0);
int &first = get_first(vect).value();
++first;
assert(get_first(vect) == 1);
const std::vector<int> cempty;
assert(!get_first(cempty));
const std::vector<int> cnon_empty{0};
assert(get_first(cnon_empty));
assert(get_first(cnon_empty) == 0);
auto &cfirst = get_first(cnon_empty).value();
static_assert(std::is_same_v<decltype(cfirst), const int &>, "");
}
That way:
get_first's return value is bool-convertible in boolean contexts, i.e. you can use it as if it were a bool to check if a reference is valid.
get_first's return value is convertible to a reference to the value stored in the container, and the value type is automatically const-qualified if the container is const-qualified. That typically is what you'd want, although don't take my word for it.
Ideally, optional_ref should be implemented using a pointer, but for demonstration purposes it was quicker to reuse std::optional and std::reference_wrapper.
But the above works (in a rudimentary fashion) under gcc, clang and msvc.
You'd use it as follows:
auto val = get_first(foo);
if (val.has_value())
{
auto &v = val.value();
// use v
}

Spamming std::move is the way to go?

About std::move, here is what I can interpret, according to http://en.cppreference.com/w/cpp/utility/move :-
If I want to transfer ownership, I have to call std::move (or in rare case, std::forward).
Responsibility of std::move is calling operator=(A&& other).
The most essential step of the move operation is supposed to be implemented in operator=(A&&).
It is tricky to ensure that operator=(A&&) would be called. It need a special converter.
There are only two converters in the C++ world that can convert variables into xvalue (the &&) : std::move and std::forward.
Question
After adding many of std::move(std::unique_ptr) in my code, I start to worry that for such basic feature like transfer ownership, I have to heavily rely on the standard library (std::).
Do I really have to use std::move to transfer ownership?
Is spamming and hard-code calling std::move in many places of code-base a correct way to go for a high-standard program?
Should std::move be encapsulated?
They are actually a single question, but ask in different perspectives.
Edit
As request, here is my trial & error. It compiled ok.
I have no problem about the code, but I worry about its approach / pattern.
https://ideone.com/y8Pcgf
class T{
public: int value;
public: T(int a=1234){
value = a;
}
};
int main() {
std::unique_ptr<T> t1 = std::unique_ptr<T>(new T(1));
void* databaseNew=operator new [](sizeof(std::unique_ptr<T>));
std::unique_ptr<T>* t1ptr=static_cast<std::unique_ptr<T>*>(databaseNew);
new (t1ptr) std::unique_ptr<T>(std::move(t1));
return 0;
}
Rule of thumb:
If you're in a deduced x-value context, use std::forward:
template<class T>
void foo(T&& t) // T is deduced x-value, so we forward it
{
bar(std::forward<T>(t));
}
Otherwise use std::move
template<class T>
void foo1(std::vector<T> v) // although vector<T> is deduced, it's not an x-value
{
bar(std::move(v)); // so move it
}
template<class T>
void foo2(std::vector<T>&& v) // although vector<T> is deduced, it's not an x-value.
// In this case an r-value reference
{
bar(std::move(v)); // so move it
}
template<class T>
void foo3(std::vector<T>& v) // although vector<T> is deduced, it's not an x-value.
// In this case an l-value reference
{
bar(std::move(v)); // so move it
}
void foo4(std::vector<int> v) // complete type
{
bar(std::move(v)); // so move it
}
void foo5(std::vector<int> const & v) // const reference
{
bar(v); // not much point in moving it. std::move would cast it
// to std::vector<int> const&&, which although is detectable
// decays to std::vector<int> const&
}
which although is detectable... what?
It is permissible, if not necessarily advisable to write code like this:
#include <iostream>
struct X
{
void foo() const &
{
// do one thing...
std::cout << "one thing\n";
}
void foo() const &&
{
// do something else...
std::cout << "or another\n";
}
};
int main()
{
const X x;
x.foo();
std::move(x).foo();
}
const r-value references do exist, it's just that no-one uses them because there is no reasonable use-case.
The need to explicitly move, of which you complain, was actually done on purpose. Before unique_ptr, STL had a horrid construct called auto_ptr. It would move ownership impllicitly, and was borderline unusable unless you really really really knew what you were doing.
To make things more usable, in most cases C++ now requires you to explicitly state that you intend on moving ownership over a container, by using std::move.
In fact, std::move is little more than a cast to an rvalue reference.
There are cases where such an explicit specification is not necessary. For example, if the container from which you take ownership is already an rvalue (e.g. - a temporary object), then no case using std::move is necessary. For example, the following doesn't compile:
std::unique_ptr<int> a;
a = new int;
But the following does, without needing a move:
std::unique_ptr<int> a;
a = std::unique_ptr<int>(new int);
The reason this does not need a call to std::move, despite invoking the move operator, is that the object we move the ownership away from is already a temporary object (i.e. - an rvalue), so no cast is necessary.
Another example is if you call a function that returns a unique_ptr. You might have to call std::move inside the function to get it into the return value, but you do not need to call std::move on the function's return value to get it into the outside unique_ptr. It is already an rvalue, and therefor no cast is necessary.

What is the proper way to use unique_ptr for raw pointers?

Having two functions for allocation and freeing a raw pointer, I want to use C++ to get dirty job done easily. I found two options to make unique_ptr handle it and neither of them looks good to me:
char *raw_alloc();
void raw_free(char *ptr);
int main (int argc, char *argv[])
{
//First option:
{
std::unique_ptr<char, decltype(&raw_free)> ptr (raw_alloc(), raw_free);
printf ("size1: %lu\n", sizeof(ptr) / sizeof(char *));
}
//Second option:
{
struct deleter { void operator()(char *ptr) { raw_free(ptr); } };
std::unique_ptr<char, deleter> ptr (raw_alloc());
printf ("size2: %lu\n", sizeof(ptr) / sizeof(char *));
}
}
Output says that the first pointer is twice as large as the second; naturally it takes space to keep a pointer to the freeing function.
In the same time second option requires me to create stub deleter for my type. Of course, I can write a function template that does it for me:
template <typename T, void (*D)(T*)>
auto my_unique (T *ptr) {
struct deleter { void operator()(T *ptr) { D(ptr); } };
return unique_ptr<T,deleter>(ptr);
};
But why can't unique_ptr do it for me and just accept deleter function as second template argument? How do skilled C++ people handle raw pointers?
I usually use the second approach, but I'd keep the Deleter class in the global scope near the declarations of raw_alloc and raw_free. It would probably help to define a raw_unique_ptr shorthand:
char *raw_alloc();
void raw_free(char *ptr);
struct raw_releter
{ void operator()(char * const ptr) const noexcept { raw_free(ptr); } };
using raw_unique_ptr = std::unique_ptr<char, raw_releter>;
std::unique_ptr by default contains a different implementation for when it doesn't contain a deleter than for when it does. As the version with deletor stores the deleter as the passed template type, the actual question boils down to the deducing of the class template parameter from the constructor. (See this thread)
As you have mentioned, the first option takes more space than the second, however I would not consider this a problem as compilers can optimize quite a lot.
Using your implementation of my_unique() is a correct approach to reduce the size of std::unique_ptr as it doesn't store the ptr to the function, however it assumes a real function pointer, while it doesn't cover using lambdas as deleter. (This is not a problem, though it's a limitation of this approach)
Another approach is using an std::shared_ptr, which doesn't require to know the how the destructor looks like, however it always stores the deleter and will take a lot of memory at the advantage of not having to know the actual implementation of the deleter.
In my opinion, I would use something similar to your option 1 or 2 when the std::unique_ptr would be locally. If this would be part of the API, it's harder to choose an implementation, and all three options (option 1, 2 or std::shared_ptr) are interesting. I would go for the std::shared_ptr as you can combine it with other APIs which have their own deleter. (Consider a factory which transforms data in either JSON or XML, which both a different function to cleanup the char* to the generated stream)
A different approach, similar to the std::shared_ptr is creating a std::unique_ptr<T, std::function<void(T*)>>, which contains a performance overhead as result of the std::function and most likely a large memory overhead compared to the original options.
You can wrap a raw pointer into class with overloaded class-specific new and delete operators:
char* raw_alloc()
{
// Don't allocate anything, just a demonstration of approach
return (char*)"Hello, World!";
}
void raw_free(char* ptr)
{
cout << ptr << endl;
}
class WrappedRawPointer
{
public:
static void* operator new(std::size_t)
{
return raw_alloc();
}
static void operator delete(void* ptr, std::size_t)
{
raw_free((char*)ptr);
}
};
int main() {
// Will print "Hello, World!" twice: once for explicit
// printing and once for deleter.
auto ptr = unique_ptr<WrappedRawPointer>(new WrappedRawPointer());
cout << (const char*) ptr.get() << endl;
return 0;
}
I believe that this approach is better since it decouples away allocation/deallocation logic, but at the same time doesn't impose any significant performance loses.

Raw pointer lookup for sets of unique_ptrs

I often find myself wanting to write code like this:
class MyClass
{
public:
void addObject(std::unique_ptr<Object>&& newObject);
void removeObject(const Object* target);
private:
std::set<std::unique_ptr<Object>> objects;
};
However, much of the std::set interface is kind of useless with std::unique_ptrs since the lookup functions require std::unique_ptr parameters (which I obviously don't have because they're owned by the set itself).
I can think of two main solutions to this.
Create a temporary unique_ptr for lookup. For example, the above removeObject() could be implemented like:
void MyClass::removeObject(const Object* target)
{
std::unique_ptr<Object> targetSmartPtr(target);
objects.erase(targetSmartPtr);
targetSmartPtr.release();
}
Replace the set with a map of raw pointers to unique_ptrs.
// ...
std::map<const Object*, std::unique_ptr<Object>> objects;
};
However, both seem slightly stupid to me. In solution 1, erase() isn't noexcept, so the temporary unique_ptr might delete the object it doesn't really own, and 2 requires double the storage for the container unnecessarily.
I know about Boost's pointer containers, but their current features are limited compared to modern C++11 standard library containers.
I was recently reading about C++14 and came across "Adding heterogeneous comparison lookup to associative containers". But form my understanding of it, the lookup types must be comparable to the key types, but raw pointers aren't comparable to unique_ptrs.
Anyone know of a more elegant solution or an upcoming addition to C++ that solves this problem?
In C++14, std::set<Key>::find is a template function if Compare::is_transparent exists. The type you pass in does not need to be Key, just equivalent under your comparator.
So write a comparator:
template<class T>
struct pointer_comp {
typedef std::true_type is_transparent;
// helper does some magic in order to reduce the number of
// pairs of types we need to know how to compare: it turns
// everything into a pointer, and then uses `std::less<T*>`
// to do the comparison:
struct helper {
T* ptr;
helper():ptr(nullptr) {}
helper(helper const&) = default;
helper(T* p):ptr(p) {}
template<class U, class...Ts>
helper( std::shared_ptr<U,Ts...> const& sp ):ptr(sp.get()) {}
template<class U, class...Ts>
helper( std::unique_ptr<U, Ts...> const& up ):ptr(up.get()) {}
// && optional: enforces rvalue use only
bool operator<( helper o ) const {
return std::less<T*>()( ptr, o.ptr );
}
};
// without helper, we would need 2^n different overloads, where
// n is the number of types we want to support (so, 8 with
// raw pointers, unique pointers, and shared pointers). That
// seems silly:
// && helps enforce rvalue use only
bool operator()( helper const&& lhs, helper const&& rhs ) const {
return lhs < rhs;
}
};
then use it:
typedef std::set< std::unique_ptr<Foo>, pointer_comp<Foo> > owning_foo_set;
now, owning_foo_set::find will accept unique_ptr<Foo> or Foo* or shared_ptr<Foo> (or any derived class of Foo) and find the correct element.
Outside of C++14, you are forced to use the map to unique_ptr approach, or something equivalent, as the signature of find is overly restrictive. Or write your own set equivalent.
Another possibility, close to the accepted answer, but a little different and simplified.
We can exploit the fact that standard comparator std::less<> (with no template arguments) is transparent. Then, we can supply our own comparison functions in the global namespace:
// These two are enough to be able to call objects.find(raw_ptr)
bool operator<(const unique_ptr<Object>& lhs, const Object* rhs) {
return std::less<const Object*>()(lhs.get(), rhs);
}
bool operator<(const Object* lhs, const unique_ptr<Object>& rhs) {
return std::less<const Object*>()(lhs, rhs.get());
}
class MyClass
{
// ...
private:
std::set<std::unique_ptr<Object>, std::less<>> objects; // Note std::less<> here
};
You can try to use boost::multi_index_container with additional indexing by Object*.
Something like this:
typedef std::unique_ptr<Object> Ptr;
typedef multi_index_container<
Ptr,
indexed_by<
hashed_unique<Ptr>,
ordered_unique<const_mem_fun<Ptr,Object*,&Ptr::get> >
>
> Objects;
Fore more information see Boost Multi-index Containers documentation
Or may be you can use std::shared_ptr everywhere, or use raw pointers in set instead?
Why you need to lookup by raw pinter? If you store it anywhere and check that object with this pointer is valid then better to use std::shared_ptr for storing in container and std::weak_ptr for other objects. In this case before usage you don't need lookup by raw pointer at all.
While definitely a hack, I just realized it's possible to construct a temporary "dumb" unique_ptr with placement new and not risk de-allocation. removeObject() could be written something like this:
void MyClass::removeObject(const Object* target)
{
alignas(std::unique_ptr<Object>)
char dumbPtrData[sizeof(std::unique_ptr<Object>)];
objects.erase(
*::new (dumbPtrData) std::unique_ptr<Object>(const_cast<Object *>(target)));
}
This solution would work for std::unordered_set, std::map keys, and std::unordered_map keys as well, all using standard C++11 only, with practically zero unnecessary overhead.
UPDATE 2: Yakk is correct, there is no way to do this with standard C++11 containers without significant compromises. Either something will run in linear time in the worst case or there are those workarounds that you write in your question.
There are two workarounds that I would consider.
I would try a sorted std::vector, similarly to boost::container::flat_set. Yes, the inserts / erases will be linear time in the worst case. Still, it might be much faster than you probably think: Contiguous containers are very cache friendly compared to node based containers, such as std::set. Please read what they write at boost::container::flat_set. Whether this compromise is acceptable for you, I cannot tell / measure.
Others also mentioned std::share_ptr. I personally try to avoid them, mainly because "a shared pointer is as good as a global variable" (Sean Parent). Another reason why I don't use them is because they are heavy weight, partly because of all the multi-threading stuff that I usually don't need. However, boost::shared_ptr, when BOOST_SP_DISABLE_THREADS is defined, removes all that overhead associated with multi-threading. I believe using boost::shared_ptr would be the easiest solution in your case.
UPDATE: As Yakk kindly pointed out, my approach has linear time complexity... :(
(The first version.)
You can do it by passing a custom comparator to std::lower_bound(). Here is a rudimentary implementation how:
#include <algorithm>
#include <cassert>
#include <iostream>
#include <memory>
#include <set>
#include <string>
using namespace std;
template <typename T>
class Set {
private:
struct custom_comparator {
bool operator()(const unique_ptr<T>& a, const T* const & b){
return a.get() < b;
}
} cmp;
set<unique_ptr<T>> objects; // decltype at begin() and end()
// needs objects to be declared here
public:
auto begin() const -> decltype(objects.begin()) { return objects.begin(); }
auto end() const -> decltype(objects.end() ) { return objects.end(); }
void addObject(unique_ptr<T>&& newObject) {
objects.insert(move(newObject));
}
void removeObject(const T* target) {
auto pos = lower_bound(objects.begin(), objects.end(), target, cmp);
assert (pos!=objects.end()); // What to do if not found?
objects.erase(pos);
}
};
void test() {
typedef string T;
Set<T> mySet;
unique_ptr<T> a{new T("a")};
unique_ptr<T> b{new T("b")};
unique_ptr<T> c{new T("c")};
T* b_ptr = b.get();
mySet.addObject(move(a));
mySet.addObject(move(b));
mySet.addObject(move(c));
cout << "The set now contains: " << endl;
for (const auto& s_ptr : mySet) {
cout << *s_ptr << endl;
}
mySet.removeObject(b_ptr);
cout << "After erasing b by the pointer to it:" << endl;
for (const auto& s_ptr : mySet) {
cout << *s_ptr << endl;
}
}
int main() {
test();
}
You're using unique pinters here. This means, your set has unique ownership of objects. Now, this should mean that if object does exist, it's either in the set or you have unique pointer with it. You don't even need to look up the set in this case.
But to me it looks like it's not hte case. I suppose you're better off with shared pointer in this case. Just store shared pointers and pass them around since someone beside this set clearly stores them.