New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
std::allocate_shared can't allocate object with private constructor #41245
Comments
I think the issue is Because you cannot have a template class inside another class it means your I agree that the static_assert needs to either change or be removed. Maybe we can add one for rebindability. |
Please pardon my lack of knowledge in this area, why is the std::allocator::rebind not sufficient in this case ? |
Yes, you are right. That should satisfy the requirements. I misdiagnosed the issue because of the version of libc++ I am using (I made some modifications to std::allocator). Anyway, I think I found the issue (for real this time). std::allocate_shared passes the args (or no args in this case) to the constructor of T (in this case that is How do you get around this? There is also a shared_ptr constructor that accepts a pointer, a deleter, and an allocator. default_delete will run into the same issue because the destructor is private, but you can get around this as well. See the updated code below: std::shared_ptr Factory::allocate()
} I provided shared_ptr a nullptr, but you can change that to a pointer you create using whatever method you want. |
Thinking more about this though, and trying to understand rebind, the rebind member is defined in http://eel.is/c++draft/allocator.requirements#tab:utilities.allocator.requirements as "typename X::template rebind::other" returning a Y, where for all U (including T), Y::template rebind::other is X. As my struct Allocator inherits from std::allocator, tn the forward direction std::allocator::rebind::other will return an std::allocator, which is fine. However, in the reverse direction, std::allocator::rebind::other will return an std::allocator, which can't allocate an instance of Private due to the private constructor and destructor. My allocator thus violates the allocator requirements, I would need to provide a rebind member where rebind::other::rebind::other would return an Allocator, not an std::allocator. I tried this (moving everything to the global scope as I can't define template structures in a function) and miserably failed :-) struct Allocator; template template template struct Allocator : std::allocator {
}; The compiler, rightfully I believe, complained that I "cannot specialize (with 'template<>') a member of an unspecialized template". There is nothing in the C++ standard, as far as I can tell, that requires std::allocate_shared to not construct the object by rebinding the allocator to a class that inherits from T (or embeds a T member), as libc++ does, and nothing the requires it to do so, so this may be a grey area. Provided that I could provide an allocator implementation that satisfies the rebind constraint, libc++ should accept it and allow me to construct the Private instance with allocate_shared. However, I don't know if providing such an allocator is feasible. Is this an unsolvable problem ?
But that won't solve my issue, as I need to use allocate_shared in order to later use shared_from_this. Otherwise I would have simply gone for std::shared_ptr Factory::allocate() |
This being said, in my specific case, I think I can make the private object inherit from std::shared_from_this and use you above solution, minus the Allocator that isn't needed. std::shared_ptr Factory::allocate()
} This doesn't fix the issue we're discussing, but works around it in my case. |
According to the standard, those constructors will enable
Just to be clear, the issue is that |
The issue is tha clang++ fails to compile the test program, because the libc++ implementation of std::allocate_shared doesn't support objects with private constructors or destructors. The use of a custom allocator wasn't so much to allocate memory in any special way, but to provide std::allocate_shared with an object that can call the private constructor. |
Patch is up: https://reviews.llvm.org/D62760 |
I at the moment stumble over the same issue, but with a protected constructor. The usage of the allocator seems to work just fine here, but the static assert already makes that impossible. If I just remove the static_assert, the code compiles and works for me as it should (and does for libstdc++). Would it be possible as a first step to remove that static_assert again? |
I must confess that was a bit too fast ;=) Using the right file one runs into the issue that the compressed_pair construction runs into memory:2155:9: error: field of type 'test' has protected constructor inside the instantiation of memory:2258:9: note: in instantiation of function template specialization 'std::__1::__compressed_pair_elem |
This should have been fixed by https://reviews.llvm.org/D91201. Please reopen if you still see the original issue (or another issue!). commit 955dd7b
|
mentioned in issue llvm/llvm-bugzilla-archive#50369 |
Extended Description
I ran into what I believe can be a bug in the libc++ implementation of
std::allocate_shared<>. The following code fails to compile due to two
issues.
#include
class Private;
class Factory {
public:
static std::shared_ptr allocate();
};
class Private {
private:
friend class Factory;
Private() { }
~Private() { }
};
std::shared_ptr Factory::allocate()
{
struct Allocator : std::allocator {
void construct(void *p)
{
::new(p) Private();
}
void destroy(Private *p)
{
p->~Private();
}
};
}
First of all, commit 7700912 ("Land D28253 which fixes #29299
(which we mistakenly marked as fixed before)") introduced a static
assert to verify that the object type is constructible:
static_assert( is_constructible<_Tp, _Args...>::value, "Can't construct object in allocate_shared" );
This fails, as Private is not constructible in the context of the
std::allocate_shared<> implementations, due to its private constructor
and destructor. I believe that the check is incorrect, as I don't see
anything in the C++ standard that mandates the object type to be
constructible by std::allocate_shared<> itself.
I tried removing the static asserts, and that's where the real fun
began. If my understanding is correct, in order to store the pointed
data, libc++ uses an internal __shared_ptr_emplace<_Tp, _Alloc> class
that contains (through a few levels of inheritance) an instance of _Tp.
That class has its destructor implicitly deleted due to the private
nature of ~Private(). The compiler then complains about
/usr/include/c++/v1/memory:3604:7: error: deleted function '~__shared_ptr_emplace' cannot override a non-deleted function
class __shared_ptr_emplace
[...]
/usr/include/c++/v1/memory:3513:13: note: overridden virtual function is here
virtual ~__shared_weak_count();
The text was updated successfully, but these errors were encountered: