Using std::vector<std::any> to store heterogenous unique_ptrs - c++

I want to store differently typed unique_ptrs in a vector.
I attempted using std::any as follows.
#include <vector>
#include <any>
#include <memory>
class A
{
int val;
};
class B
{
float val;
};
int main()
{
std::vector<std::any> vec;
auto a = new A();
auto b = new B();
vec.push_back(std::unique_ptr<A>(a));
vec.push_back(std::unique_ptr<B>(b));
}
It is failing as follows.
main.cpp: In function 'int main()':
main.cpp:23:18: error: no matching function for call to 'std::vector<std::any>::push_back(std::unique_ptr<A>)'
23 | vec.push_back(std::unique_ptr<A>(a));
| ~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~
In file included from /usr/local/include/c++/12.1.0/vector:64,
from main.cpp:1:
/usr/local/include/c++/12.1.0/bits/stl_vector.h:1276:7: note: candidate: 'void std::vector<_Tp, _Alloc>::push_back(const value_type&) [with _Tp = std::any; _Alloc = std::allocator<std::any>; value_type = std::any]'
1276 | push_back(const value_type& __x)
| ^~~~~~~~~
/usr/local/include/c++/12.1.0/bits/stl_vector.h:1276:35: note: no known conversion for argument 1 from 'std::unique_ptr<A>' to 'const std::vector<std::any>::value_type&' {aka 'const std::any&'}
1276 | push_back(const value_type& __x)
| ~~~~~~~~~~~~~~~~~~^~~
/usr/local/include/c++/12.1.0/bits/stl_vector.h:1293:7: note: candidate: 'void std::vector<_Tp, _Alloc>::push_back(value_type&&) [with _Tp = std::any; _Alloc = std::allocator<std::any>; value_type = std::any]'
1293 | push_back(value_type&& __x)
| ^~~~~~~~~
/usr/local/include/c++/12.1.0/bits/stl_vector.h:1293:30: note: no known conversion for argument 1 from 'std::unique_ptr<A>' to 'std::vector<std::any>::value_type&&' {aka 'std::any&&'}
1293 | push_back(value_type&& __x)
| ~~~~~~~~~~~~~^~~
main.cpp:24:18: error: no matching function for call to 'std::vector<std::any>::push_back(std::unique_ptr<B>)'
24 | vec.push_back(std::unique_ptr<B>(b));
| ~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~
/usr/local/include/c++/12.1.0/bits/stl_vector.h:1276:7: note: candidate: 'void std::vector<_Tp, _Alloc>::push_back(const value_type&) [with _Tp = std::any; _Alloc = std::allocator<std::any>; value_type = std::any]'
1276 | push_back(const value_type& __x)
| ^~~~~~~~~
/usr/local/include/c++/12.1.0/bits/stl_vector.h:1276:35: note: no known conversion for argument 1 from 'std::unique_ptr<B>' to 'const std::vector<std::any>::value_type&' {aka 'const std::any&'}
1276 | push_back(const value_type& __x)
| ~~~~~~~~~~~~~~~~~~^~~
/usr/local/include/c++/12.1.0/bits/stl_vector.h:1293:7: note: candidate: 'void std::vector<_Tp, _Alloc>::push_back(value_type&&) [with _Tp = std::any; _Alloc = std::allocator<std::any>; value_type = std::any]'
1293 | push_back(value_type&& __x)
| ^~~~~~~~~
/usr/local/include/c++/12.1.0/bits/stl_vector.h:1293:30: note: no known conversion for argument 1 from 'std::unique_ptr<B>' to 'std::vector<std::any>::value_type&&' {aka 'std::any&&'}
1293 | push_back(value_type&& __x)
|
Is this possible with std::any? Or is there any alternative? I am unable to use std::variant as I don't know all the types to be stored upfront.
Edit:
I just want to use the unique_ptr vector to enforce that objects will be cleaned up at program exit (the vector will be alive till program exit). The consumer code will use direct references to a and b so I don't need to access/enumerate object references via the vector.

std::unique_ptr cannot be used as a std::any, because the latter requires the value type to be copy-constructible, which std::unique_ptr is not.
Given the use case that you described:
A straight-forward solution would be to use std::shared_ptr instead, which is copy-constructible.
However, in that case std::any is not necessary at all. All std::shared_ptr instances can always be converted to std::shared_ptr<void>. The deleter is type-erased and will still be called as expected:
std::vector<std::shared_ptr<void>> vec;
auto new_a = std::make_shared<A>();
A* a = new_a.get();
vec.push_back(std::move(new_a));
auto new_b = std::make_shared<B>();
B* b = new_b.get();
vec.push_back(std::move(new_b));
// use a and b here, assuming that vec outlives them
The element needs to be constructed directly into a shared_ptr (e.g. with std::make_shared), because otherwise an exception inbetween the new expression and construction of the shared_ptr will cause a memory leak. std::move is optional.
However, std::shared_ptr is much more than you really need. std::unique_ptr doesn't have a void instance that you can use because the deleter's type is not erased, but you can achieve the same effect by writing a std::unique_ptr equivalent which derives from a single non-template base class with virtual destructor. Then you can use that base class as element type in your vector.
(If you are confortable with an additonal indirection, this can be easily implemented by using std::unique_ptr itself in the derived template and then using std::unique_ptr<base> in the base class. If you don't want the indirection, then I think you'll have to implement it from scratch. I can't think of a helpful standard library functionality.)
It is also possible to implement your own std::any equivalent that supports non-copyable types. std::any simply made the decision to support copying and once that decision is made all potentially contained types must support it. But that's more complex than what I suggested above.
However, an even easier solution, assuming that is acceptable design and performance for you, is to have all classes A, B, etc. that you intent to store derive from some Base with virtual destructor, in which case simply std::vector<std::unique_ptr<Base>> will also do it. In this case the virtual destructor is however required! Otherwise you will have undefined behavior!

If you need it only to destroy objects, then you can do something much simpler, for example like this:
#include <iostream>
#include <vector>
#include <functional>
#include <utility>
class A
{
public:
~A() {std::cout << "~A()\n";}
int val;
};
class B
{
public:
~B() {std::cout << "~B()\n";}
float val;
};
class ObjKeeper
{
public:
~ObjKeeper() {
for (auto& f : objs) {
f();
}
}
template<typename T>
void addObj(T *obj) {
objs.push_back([obj]() {delete obj;});
}
private:
std::vector<std::function<void()>> objs;
};
int main()
{
ObjKeeper keep;
auto a = new A();
auto b = new B();
keep.addObj(a);
keep.addObj(b);
}

In addition to what others wrote, I'd also say that storing a smart pointer instance in
std::vector<std::any> vec;
is an anti-pattern or a code smell.
This is because a std::any instance, similarly to a smart pointer instance, will take over unique ownership over the object it holds, while a smart pointer will only take over the ownership of a pointer. However, if you copy a std::any instance, unlike a std::unique_ptr instance, it will want to copy the object it holds. Therefore, I'd just try doing this first. A std::any instance will destroy the object it holds, so there is no need for a smart pointer in that regard.
Your application design could probably be improved, but writing a custom std::any, that behaves differently than the standard one (you could forbid copying or implement shared ownership, for example), is a worthy project, as type erasure is a recurring topic in c++.
Lastly, you can use memory sanitizers, if you're worried about memory leaks.

Related

C++ implicit conversion of unique_ptr / auto_ptr to base type not working?

Consider the following code:
#include <memory>
struct A {};
struct B : public A {};
void func( std::auto_ptr< A > ptr ) {}
int main() {
std::auto_ptr< B > b( new B() );
func( b );
}
I know that auto_ptr is removed in C++17 and was deprecated for a long time, however, I am using boost-python, which does not support unique_ptr in its interfaces (e.g., see this). I am using g++ 7.5.
Due to the conversion constructor defined by auto_ptr (see (3)), implicit conversion should be possible. However, I get the error:
error: conversion from ‘std::auto_ptr’ to ‘std::auto_ptr’ is ambiguous
If I use unique_ptr instead, which defines a similar constructor (see (6)), I get a similar error:
error: could not convert ‘b’ from ‘std::unique_ptr’ to ‘std::unique_ptr’
Why is that?
It appears that the compiler thinks that it is ambiguous, because the operator() conversion and constructor type conversion are ranked the same in auto_ptr. The compiler error says it quite clearly that it couldn't pick between the 2 conversions:
../src/test_curl_pop3.cpp:26:10: error: conversion from ‘std::auto_ptr<B>’ to ‘std::auto_ptr<A>’ is ambiguous
func( b );
^
In file included from /usr/include/c++/5/memory:85:0,
from ../src/test_curl_pop3.cpp:9:
/usr/include/c++/5/backward/auto_ptr.h:279:9: note: candidate: std::auto_ptr< <template-parameter-1-1> >::operator std::auto_ptr<_Up>() [with _Tp1 = A; _Tp = B]
operator auto_ptr<_Tp1>() throw()
^
/usr/include/c++/5/backward/auto_ptr.h:125:9: note: candidate: std::auto_ptr< <template-parameter-1-1> >::auto_ptr(std::auto_ptr<_Up>&) [with _Tp1 = B; _Tp = A]
auto_ptr(auto_ptr<_Tp1>& __a) throw() : _M_ptr(__a.release()) { }
^
../src/test_curl_pop3.cpp:20:6: note: initializing argument 1 of ‘void func(std::auto_ptr<A>)’
void func( std::auto_ptr<A> p ) {}

Why does lambda auto& parameter choose const overload?

I'm trying to implement a class which wraps an arbitrary type and a mutex. To access the wrapped data, one needs to pass a function object as parameter of the locked method. The wrapper class will then pass the wrapped data as parameter to this function object.
I'd like my wrapper class to work with const & non-const, so I tried the following
#include <mutex>
#include <string>
template<typename T, typename Mutex = std::mutex>
class Mutexed
{
private:
T m_data;
mutable Mutex m_mutex;
public:
using type = T;
using mutex_type = Mutex;
public:
explicit Mutexed() = default;
template<typename... Args>
explicit Mutexed(Args&&... args)
: m_data{std::forward<Args>(args)...}
{}
template<typename F>
auto locked(F&& f) -> decltype(std::forward<F>(f)(m_data)) {
std::lock_guard<Mutex> lock(m_mutex);
return std::forward<F>(f)(m_data);
}
template<typename F>
auto locked(F&& f) const -> decltype(std::forward<F>(f)(m_data)) {
std::lock_guard<Mutex> lock(m_mutex);
return std::forward<F>(f)(m_data);
}
};
int main()
{
Mutexed<std::string> str{"Foo"};
str.locked([](auto &s) { /* this doesn't compile */
s = "Bar";
});
str.locked([](std::string& s) { /* this compiles fine */
s = "Baz";
});
return 0;
}
The first locked call with the generic lambda fails to compile with the following error
/home/foo/tests/lamdba_auto_const/lambda_auto_const/main.cpp: In instantiation of ‘main()::<lambda(auto:1&)> [with auto:1 = const std::__cxx11::basic_string<char>]’:
/home/foo/tests/lamdba_auto_const/lambda_auto_const/main.cpp:30:60: required by substitution of ‘template<class F> decltype (forward<F>(f)(((const Mutexed<T, Mutex>*)this)->Mutexed<T, Mutex>::m_data)) Mutexed<T, Mutex>::locked(F&&) const [with F = main()::<lambda(auto:1&)>]’
/home/foo/tests/lamdba_auto_const/lambda_auto_const/main.cpp:42:6: required from here
/home/foo/tests/lamdba_auto_const/lambda_auto_const/main.cpp:41:11: error: passing ‘const std::__cxx11::basic_string<char>’ as ‘this’ argument discards qualifiers [-fpermissive]
s = "Bar";
^
In file included from /usr/include/c++/5/string:52:0,
from /usr/include/c++/5/stdexcept:39,
from /usr/include/c++/5/array:38,
from /usr/include/c++/5/tuple:39,
from /usr/include/c++/5/mutex:38,
from /home/foo/tests/lamdba_auto_const/lambda_auto_const/main.cpp:1:
/usr/include/c++/5/bits/basic_string.h:558:7: note: in call to ‘std::__cxx11::basic_string<_CharT, _Traits, _Alloc>& std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::operator=(const _CharT*) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]’
operator=(const _CharT* __s)
^
But the second call with the std::string& parameter is fine.
Why is that ? And is there a way to make it work as expected while using a generic lambda ?
This is a problem fundamentally with what happens with SFINAE-unfriendly callables. For more reference, check out P0826.
The problem is, when you call this:
str.locked([](auto &s) { s = "Bar"; });
We have two overloads of locked and we have to try both. The non-const overload works fine. But the const one – even if it won't be selected by overload resolution anyway – still has to be instantiated (it's a generic lambda, so to figure out what decltype(std::forward<F>(f)(m_data)) might be you have to instantiate it) and that instantiation fails within the body of the lambda. The body is outside of the immediate context, so it's not a substitution failure – it's a hard error.
When you call this:
str.locked([](std::string& s) { s = "Bar"; });
We don't need to look at the body at all during the whole process of overload resolution – we can simply reject at the call site (since you can't pass a const string into a string&).
There's not really a solution to this problem in the language today – you basically have to add constraints on your lambda to ensure that the instantiation failure happens in the immediate context of substitution rather than in the body. Something like:
str.locked([](auto &s) -> void {
s = "Bar";
});
Note that we don't need to make this SFINAE-friendly - we just need to ensure that we can determine the return type without instantiating the body.
A more thorough language solution would have been to allow for "Deducing this" (see the section in the paper about this specific problem). But that won't be in C++20.

How to store a smart pointer to map of string to member function in a class so that it doesn't fail due to an incomplete type?

Background
I have been writing a StateMachine whose transition table is loaded at runtime. The action to take upon each transition is stored as a string. The string is converted to a std::function object that points to a member function of the state machine class. When an event occurs, and results in a transition, that function is invoked.
Problem
I have successfully used this strategy before to decide which function is called at run time. Unfortunately, I've been running into the following error:
error: return type 'XStMachine::TrFunc {aka class std::function<void (XStMachine::*)(const EventData&)>}' is incomplete
Or
invalid use of incomplete type
Steps Taken
I consulted Google and Stackoverflow. I got a number of ideas including taking the definition out of the place where the type is incomplete. Unfortunately, I couldn't get it to work successfully.
I tried using a raw pointer instead of a unique_ptr and found that things worked magically.
I ended up reading a little on the difference between how shared_ptr and unique_ptr handle incomplete types. I tried a shared_ptr, but that did not solve my issue either.
I tried creating a friend class to my state machine in the hope that by the time of the friend class' declaration, the type would be considered whole. I could not get this to work.
Finally, I created the following minimal example (Uncomment code to reproduce the error, please.) which demonstrates the problems I ran into: http://coliru.stacked-crooked.com/a/791092c7ca8fff24 and came to the experts! :)
Source Code
#include <iostream>
#include <string>
#include <functional>
#include <memory>
#include <map>
#include <iomanip>
using namespace std;
struct EventData
{
unsigned int x;
};
class Friendly; // Required for compiling the code. Why?
class XStMachine
{
friend class Friendly;
unique_ptr<Friendly> fPtr; //-- Doesn't compile if Friendly is not fwd. declared
unsigned int y;
unsigned int z;
public:
typedef void(XStMachine::*TrFuncPtr)(EventData const&);
typedef std::function<TrFuncPtr> TrFunc;
private:
// map<string, TrFunc> fMap; //-- Doesn't compile because TrFunc is incomplete
// unique_ptr<map<string, TrFunc>> fMap; // Doesn't compile; incomplete type.
map<string, TrFunc> *fMap; // Compiles with incomplete type.
protected:
void tranFunc1(EventData const &d)
{
y = d.x;
}
void tranFunc2(EventData const &d)
{
z = d.x;
}
public:
XStMachine()
{
// Code to init fMap
}
// The code below doesn't compile. incomplete type.
TrFunc getTranFunc(std::string const &s)
{
return (*fMap)[s];
}
~XStMachine()
{
}
};
class Friendly
{
// unique_ptr<map<string, XStMachine::TrFunc> fMap; // Doesn't compile, the type is incomplete.
public:
Friendly()
{
// Code to allocate and init fMap
}
// Dosen't compile if defined here because the return type is incomplete.
//XStMachine::TrFunc& getTranFunc(std::string const&)
//{
// return (*fMap)[s];
//}
};
// The type is incomplete -> Will this work inside a separate cpp file?
//XStMachine::TrFunc& getTranFunc(std::string const &s)
//{
// Weird - Can't access protected members though we're friends. :/
/*
static map<string, XStMachine::TrFunc> fMap = {{"tranFunc1", function(&XStMachine::tranFunc1)},
{"tranFunc2", function(&XStMachine::tranFunc2)}
};
*/
//return fMap[s];
//}
int main() {
cout << "I need to understand incomplete types." << endl;
return 0;
}
Full Error Output from Coliru (gcc 6.3, C++ 14)
main.cpp: In member function 'XStMachine::TrFunc XStMachine::getTranFunc(const string&)':
main.cpp:48:3: error: return type 'XStMachine::TrFunc {aka class std::function<void (XStMachine::*)(const EventData&)>}' is incomplete
{
^
In file included from /usr/local/include/c++/6.3.0/bits/stl_algobase.h:64:0,
from /usr/local/include/c++/6.3.0/bits/char_traits.h:39,
from /usr/local/include/c++/6.3.0/ios:40,
from /usr/local/include/c++/6.3.0/ostream:38,
from /usr/local/include/c++/6.3.0/iostream:39,
from main.cpp:1:
/usr/local/include/c++/6.3.0/bits/stl_pair.h: In instantiation of 'struct std::pair<const std::__cxx11::basic_string<char>, std::function<void (XStMachine::*)(const EventData&)> >':
/usr/local/include/c++/6.3.0/bits/stl_map.h:481:10: required from 'std::map<_Key, _Tp, _Compare, _Alloc>::mapped_type& std::map<_Key, _Tp, _Compare, _Alloc>::operator[](const key_type&) [with _Key = std::__cxx11::basic_string<char>; _Tp = std::function<void (XStMachine::*)(const EventData&)>; _Compare = std::less<std::__cxx11::basic_string<char> >; _Alloc = std::allocator<std::pair<const std::__cxx11::basic_string<char>, std::function<void (XStMachine::*)(const EventData&)> > >; std::map<_Key, _Tp, _Compare, _Alloc>::mapped_type = std::function<void (XStMachine::*)(const EventData&)>; std::map<_Key, _Tp, _Compare, _Alloc>::key_type = std::__cxx11::basic_string<char>]'
main.cpp:49:23: required from here
/usr/local/include/c++/6.3.0/bits/stl_pair.h:196:11: error: 'std::pair<_T1, _T2>::second' has incomplete type
_T2 second; /// #c second is a copy of the second object
^~~~~~
In file included from main.cpp:3:0:
/usr/local/include/c++/6.3.0/functional:1526:11: note: declaration of 'class std::function<void (XStMachine::*)(const EventData&)>'
class function;
^~~~~~~~
Objectives
Primary: Understand what is going on in the example code and fix it.
Secondary: Gain a clear understanding of what an incomplete type is so that I can:
* Solve related problems in the future.
* Know if it is safe to override unique_ptr's default deleter with a deleter that calls the default.
My lack of understanding is really getting in my way here.
Related Questions
Even though Friendly is declared as a friend within XStMachine in the example code, it has to be forward declared earlier in the program as well. Why does this happen?
Even though Friendly is declared a friend, it cannot access protected member functions of XStMachine. For instance, &XStMachine::tranFunc1 is invalid. Why?
std::function takes only a regular function type as template argument. A pointer-to-member-function type doesn't work.
Below is what might be a typical definition of std::function in the standard library:
template< class >
class function; // intentionally undefined
template< class R, class... Args >
class function<R(Args...)> // actual definition
The template argument doesn't determine what kind of function this instantiation can store, but rather how this instantiation can be called.
Any instantiation attempt with a type that is not a regular function type will produce an incomplete type. Example:
std::function <int> incomplete;
In your code you may either:
store std::function<void(EventData const&)> in the map (use std::bind to construct these objects from a pointer-to-member-function and an object pointer); or
do away with std::function altogether and store pointers-to-member-function in the map directly.

which of these compilers has a bug, according to the standard?

Given the following source code:
#include <memory>
#include <iostream>
using namespace std;
struct concept
{
virtual void perform() = 0;
};
struct model : concept, enable_shared_from_this<model>
{
void perform() override {
cout << "my pointer is " << shared_from_this().get() << endl;
}
};
int main(int argc, const char * argv[])
{
// shared_ptr<concept> concept_ptr = make_shared<model>();
shared_ptr<concept> concept_ptr { new model };
concept_ptr->perform();
return 0;
}
Compiling under gcc, this code compiles and associates the internal weak_ptr with the address of model.
Under clang the code will not compile (error message included at the end)
If you replace the initialisation of concept_ptr with shared_ptr<concept> concept_ptr = make_shared<model>(); it will compile on both.
Which is correct?
edit:
My version of clang is the one that ships with Xcode:
$ clang --version
Apple LLVM version 5.1 (clang-503.0.40) (based on LLVM 3.4svn)
Target: x86_64-apple-darwin13.3.0
Thread model: posix
edit2:
Just wanted to say thanks to everyone for contributing.
If you're interested, the reason I want to do this is that I want an opaque interface to an implementation with shared-handle semantics. Some implementations (async ones) require that callback objects ensure that the implementation object still exists (argues for shared_from_this and weak_ptr::lock). Other implementations do not require this. I wanted to avoid encumbering the concept (public interface) with the enable_shared_from_this<> base class, since that couples implementation with interface - a known evil.
In most cases, it's reasonable to use make_shared to create the implementation object. In rarer cases that require a custom destructor, the following seems portable:
auto concept_ptr = static_pointer_cast<concept>(shared_ptr<model> {
new model ,
[](model* self) {
// some_deletion_operation on self;
} });
appendix:
error message on clang:
In file included from /Users/richardh/Documents/dev/Scratchpad/tryit/tryit/try2.cpp:1:
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/c++/v1/memory:4013:35: error: no viable overloaded '='
__e->__weak_this_ = *this;
~~~~~~~~~~~~~~~~~ ^ ~~~~~
...etc...
I understand that libstdc++ follows the standard more closely here.
Concerning the requirements for
shared_ptr<T> shared_from_this();
shared_ptr<const T> shared_from_this() const;
both N3337 §20.7.2.4 (7) and N3936 §20.8.2.5 (7) only require
enable_shared_from_this<T> shall be an accessible base class
of T. *this shall be a subobject of an object t of type T. There shall
be at least one shared_ptr instance p that owns &t.
There is no requirement named that one shared_ptr owning &t actually has to be a shared_ptr<T> or shared_ptr<A_to_T_Convertible>.
And that very function is the core of that class' functionality.
Thus, given Tp as the actual param of the enabled_shared_from_this and Tp1 as the actual parameter of that owning shared_ptr, is_convertible<Tp1, Tp>::value == true, let alone is_same<Tp1, Tp>::value == true, is not required by the standard, same for respective pointers.
And indeed, the full output of clang++ using libc++ has
/usr/local/bin/../include/c++/v1/memory:3997:35: error: no viable overloaded '='
__e->__weak_this_ = *this;
~~~~~~~~~~~~~~~~~ ^ ~~~~~
/usr/local/bin/../include/c++/v1/memory:4035:5: note: in instantiation of
function template specialization
'std::__1::shared_ptr<concept>::__enable_weak_this<model>' requested here
__enable_weak_this(__p);
^
[...]enable_shared.cxx:34:25: note: in instantiation
of function template specialization
'std::__1::shared_ptr<concept>::shared_ptr<model>' requested here
shared_ptr<concept> model_ptr1(new model);
^
/usr/local/bin/../include/c++/v1/memory:4942:15: note: candidate function not
viable: no known conversion from 'std::__1::shared_ptr<concept>' to 'const
std::__1::weak_ptr<model>' for 1st argument
weak_ptr& operator=(weak_ptr const& __r) _NOEXCEPT;
^
/usr/local/bin/../include/c++/v1/memory:4953:15: note: candidate function not
viable: no known conversion from 'std::__1::shared_ptr<concept>' to
'std::__1::weak_ptr<model>' for 1st argument
weak_ptr& operator=(weak_ptr&& __r) _NOEXCEPT;
^
/usr/local/bin/../include/c++/v1/memory:4949:9: note: candidate template
ignored: could not match 'weak_ptr' against 'shared_ptr'
operator=(weak_ptr<_Yp> const& __r) _NOEXCEPT;
^
/usr/local/bin/../include/c++/v1/memory:4960:9: note: candidate template
ignored: could not match 'weak_ptr' against 'shared_ptr'
operator=(weak_ptr<_Yp>&& __r) _NOEXCEPT;
^
/usr/local/bin/../include/c++/v1/memory:4967:13: note: candidate template
ignored: disabled by 'enable_if' [with _Yp = concept]
is_convertible<_Yp*, element_type*>::value,
^
So libc++ here wants
is_convertible<Tp1* /*= Base* = concept**/, Tp* /*= Derived* = model* */>
which of course fails here, that the run-time *this of that very shared_ptr<Tp1> would be dynamic_cast-able to Tp* is out of ansatz here.
From this perspective, it's also clear why shared_ptr<concept> concept_ptr = make_shared<model>(); doesn't suffer from that; on the rhs there is a shared_ptr<Tp /* = derived = model */> constructor and for that ptr is_convertible holds.
libstdc++ doesn't suffer from this, because it passes the argument, thus its type (= Derived = model), of the shared_ptr<Tp1 /* = Base = concept*/> constructor down to the internal weak_ptr<T /*= Derived = model*/> assignment, not the shared_ptr in construction.
https://github.com/mirrors/gcc/blob/master/libstdc%2B%2B-v3/include/bits/shared_ptr_base.h#L848
template<typename _Tp, _Lock_policy _Lp>
class __shared_ptr
{
https://github.com/mirrors/gcc/blob/master/libstdc%2B%2B-v3/include/bits/shared_ptr_base.h#L858
template<typename _Tp1>
explicit __shared_ptr(_Tp1* __p)
: _M_ptr(__p), _M_refcount(__p)
{
__glibcxx_function_requires(_ConvertibleConcept<_Tp1*, _Tp*>)
static_assert( !is_void<_Tp1>::value, "incomplete type" );
static_assert( sizeof(_Tp1) > 0, "incomplete type" );
__enable_shared_from_this_helper(_M_refcount, __p, __p);
}
https://github.com/mirrors/gcc/blob/master/libstdc%2B%2B-v3/include/bits/shared_ptr_base.h#L1459
template<typename _Tp, _Lock_policy _Lp>
class __enable_shared_from_this
{
https://github.com/mirrors/gcc/blob/master/libstdc%2B%2B-v3/include/bits/shared_ptr_base.h#L1482
private:
template<typename _Tp1>
void
_M_weak_assign(_Tp1* __p, const __shared_count<_Lp>& __n) const noexcept
{ _M_weak_this._M_assign(__p, __n); }
template<typename _Tp1>
friend void
__enable_shared_from_this_helper(const __shared_count<_Lp>& __pn,
const __enable_shared_from_this* __pe,
const _Tp1* __px) noexcept
{
if (__pe != 0)
__pe->_M_weak_assign(const_cast<_Tp1*>(__px), __pn);
}
My point of view only; comments welcome.
#Richard Hodges: +1, very interesting topic

boost::intrusive_ptr constructor ambiguity using a class' 'this' pointer

The offending code:
template<typename T>
class SharedObject {
public:
typedef boost::intrusive_ptr<T> Pointer;
typedef boost::intrusive_ptr<T const> ConstPointer;
inline Pointer GetPointer() {
return Pointer(this); //Ambiguous call here
}
inline ConstPointer GetPointer() const {
return ConstPointer(this);
}
...
and used like this:
template <typename T>
class SomeClass: public SharedObject<SomeClass<T> > {
public:
static inline boost::intrusive_ptr<SomeClass<T> > Create() {
return (new SomeClass)->GetPointer();
}
};
int main()
{
auto v = SomeClass<int>::Create();
}
GCC (4.4.1) with boost 1.41 gives this error upon instatiating the first (non-const) version of GetPointer():
error: call of overloaded ‘intrusive_ptr SharedObject<SomeClass<int> >* const)’ is ambiguous
boost/smart_ptr/intrusive_ptr.hpp:118: note: candidates are: boost::intrusive_ptr<T>::intrusive_ptr(boost::intrusive_ptr<T>&&) [with T = SomeClass<int>] <near match>
boost/smart_ptr/intrusive_ptr.hpp:94: note: boost::intrusive_ptr<T>::intrusive_ptr(const boost::intrusive_ptr<T>&) [with T = SomeClass<int>] <near match>
boost/smart_ptr/intrusive_ptr.hpp:70: note: boost::intrusive_ptr<T>::intrusive_ptr(T*, bool) [with T = SomeClass<int>] <near match>
To my less than arcane skills in C++, I can't see why there is any ambiguity at all. The two canditates at lines 188 and 94 takes an existing intrusive_ptr rvalue reference, which SharedObject::this certainly is not. The final candidate however is a perfect match (the bool argument is optional).
Anyone care to enlighten me as to what the problem is?
EDIT+answer: I finally realized that in
inline Pointer GetPointer() {
return Pointer(this); //Ambiguous call here
}
this refers to SharedObject while the Pointer typedef is SomeClass. (Which is pretty much what Butterworth pointed out right away).
inline Pointer GetPointer() {
return Pointer(static_cast<C*>(this));
}
Since I know this to really be SomeClass, inheriting from SharedObject, a static_cast makes the template class go 'round.
When you say:
typedef boost::intrusive_ptr<T> Pointer;
you are declaring a type which is an intrusive pointer to an int (because T is an int at that point), when the template is instantiated in your code. Your SharedObject class is not an int, so you can't instantiate such an intrusive pointer using this.
Edit: OK, I misunderstood your code, I'll try again. At:
return Pointer(this); //Ambiguous call here
this is a SharedObject , as per the error messages, however the pointer is typedefed to a SomeClass I think.
Your code is incredibly hard to understand - whatever it is you are trying to do, there must be a simpler way. And you seem to be missing a virtual destructor (and maybe a virtual function) in the base class.