I came across this answer Prevent moving of a unique_ptr C++11. However while trying it out on a compiler online, this works with C++11(std::move compiler error) but with C++17, I'm seeing that the std::move below is successful. Shouldn't the compiler throw an error on that line? Also if some semantics have changed in C++17, what is the correct way to create a non movable unique_ptr in C++17 and onward.
template <typename T>
using scoped_ptr = const std::unique_ptr<T>;
int main()
{
auto p = scoped_ptr<int>(new int(5));
auto p2 = std::move(p); // should be error?
std::cout << *p2 << std::endl; // 5
return 0;
}
You can try it online here.
p is not const. See here for it to fail the way you expect.
auto deduces like a template<class T>void foo(T) does. T is never deduced as const, and neither is auto p=.
Meanwhile, the auto p = line works because you compiled it in c++17 mode. In c++11 it does not compile. This is because how prvalues differ in 17; some call the difference guaranteed elision.
If you want an immobile unique ptr:
template<class T, class D>
struct immobile_ptr:private std::unique_ptr<T, D>{
using unique_ptr<T>::operator*;
using unique_ptr<T>::operator->;
using unique_ptr<T>::get;
using unique_ptr<T>::operator bool;
// etc
// manually forward some ctors, as using grabs some move ctors in this case
};
template<class T, class...Args>
immobile_ptr<T> make_immobile_ptr(Args&&...args); // todo
an alternative might be to take a unique ptr with an immobile destroyer.
template<class X>
struct nomove_destroy:std::destroy<T>{
nomove_destroy(nomove_destroy&&)=delete;
nomove_destroy()=default;
nomove_destroy& operator=(nomove_destroy&&)=delete;
};
template<class T>
using nomove_ptr=std::unique_ptr<T,nomove_destroy<T>>;
But I am uncertain if that will work.
Note that p is declared as non-reference type, the const part of the argument scoped_ptr<int>(new int(5)) is ignored in type deduction. Then the type deduction result for p is std::unique_ptr<int>, not const std::unique_ptr<int> (i.e. scoped_ptr<int> as you expected).
What you want might be
auto& p = scoped_ptr<int>(new int(5)); // p is of type const std::unique_ptr<int>& now
Welcome to the world of type deduction in C++. Try
auto & p = scoped_ptr<int>(new int(5));
or
auto && p = scoped_ptr<int>(new int(5));
instead. This lecture may be helpful: https://www.youtube.com/watch?v=wQxj20X-tIU
Related
I have a function that creates a new object of the underlying type of P. Here P is a dereferencable type like a pointer or a smart pointer.
template<typename P>
auto make_new()
For example, for pointers and smart pointers,
struct A
{
int a = 3;
};
A* a = make_new<A*>();
std::cout << a->a << std::endl;
delete a;
std::shared_ptr<A> b = make_new<std::shared_ptr<A>>();
std::cout << b->a << std::endl;
Now, for shared pointers, I would implement make_new as the following,
template<typename P>
auto make_new()
{
using Ptype = typename P::element_type;
return P(new Ptype);
}
which doesn't work for pointers.
Now, something that works for both pointers and smart pointers,
template<typename P>
auto make_new()
{
using Ptype = typename std::remove_reference<decltype(*P())>::type;
return P(new Ptype);
}
but doesn't work for std::optional.
Is there a canonical way of getting the underlying type of a de-referencable object?
I know that * and -> can be overloaded to anything and there is no guarantee that constructor works like above, or makes sense to do.
Just want to know if there is a way and am not just finding it, or just doing something dumb.
Resolving the element type on both pointers and classes
Target. Our goal is to write a using template which takes a dereference-able type as input, and returns the element type.
template<class T>
using element_type_t = /* stuff */;
Method. We can use SFINAE to check if there's an element_type property, and if there's not, we fall back to using std::remove_reference<decltype(*P())>().
// This is fine to use in an unevaluated context
template<class T>
T& reference_to();
// This one is the preferred one
template<class Container>
auto element_type(int)
-> typename Container::element_type;
// This one is the fallback if the preferred one doesn't work
template<class Container>
auto element_type(short)
-> typename std::remove_reference<decltype(*reference_to<Container>())>::type;
Once we have this function, we can write element_type_t by just getting the return type of element_type.
// We alias the return type
template<class T>
using element_type_t = decltype(element_type<T>(0));
Why can't we always get the element_type by dereferencing it? If you try to always get the value type using the * operator, that could cause issues with things like the iterator for std::vector<bool>, which returns an object that acts like a bool, but encapsulates bit manipulations. In these cases, the element type is different than the type returned by dereferencing it.
Determining if the constructor takes a pointer or a value
The reason your code fails with std::optional is because std::optional's constructor takes the value itself, rather than a pointer to the value.
In order to determine which constructor we need, we use SFINAE again to make the determination.
// Base case - use new operator
template<class Container>
auto make_new_impl(int)
-> decltype(Container{new element_type_t<Container>})
{
return Container{new element_type_t<Container>};
}
// Fallback case, where Container takes a value
template<class Container>
auto make_new_impl(long)
-> decltype(Container{element_type_t<Container>()})
{
return Container{element_type_t<Container>()};
}
Now, we can write make_new so that it calls make_new_impl:
template<class Container>
auto make_new() {
return make_new_impl<Container>(0);
}
Example. We can now use make_new to make either std::optional, std::shared_ptr, or even a regular pointer.
#include <optional>
#include <memory>
int main() {
// This works
int* ptr = make_new<int*>();
// This works too
std::shared_ptr<int> s = make_new<std::shared_ptr<int>>();
// This also works
std::optional<int> o = make_new<std::optional<int>>();
}
Here is a test file from gcc, live demo
struct do_nothing
{
template <class T>
void operator()(T*) {}
};
int
main()
{
int i = 0;
std::unique_ptr<int, do_nothing> p1(&i);
std::unique_ptr<int> p2;
static_assert(!std::is_assignable<decltype(p2), decltype(p1)>::value, ""); // note ! here.
}
std::is_assignable
If the expression std::declval<T>() = std::declval<U>() is well-formed in unevaluated context, provides the member constant value equal true. Otherwise, value is false. Access checks are performed as if from a context unrelated to either type.
std::declval:
template<class T>
typename std::add_rvalue_reference<T>::type declval() noexcept;
The return type is T&& unless T is (possibly cv-qualified) void, in which case the return type is T.
Let's look at MoveAssignOnly:
struct MoveAssignOnly {
MoveAssignOnly &operator=(MoveAssignOnly &) = delete;
MoveAssignOnly &operator=(MoveAssignOnly &&) = default;
};
int main()
{
static_assert(
not std::is_assignable<MoveAssignOnly, MoveAssignOnly>::value, "");
}
live demo:
error: static_assert failed due to requirement '!std::is_assignable<MoveAssignOnly, MoveAssignOnly>::value'
Yes, it fails to compile because it provides a move assignment
Let's return to the gcc's test file and std::unique_ptr. As we know, std::unique_ptr also has move assignments.
However, unlike struct MoveAssignOnly, static_assert(!std::is_assignable<decltype(p2), decltype(p1)>::value, "");(more clearly, static_assert(!std::is_assignable<std::unique_ptr<int>, std::unique_ptr<int, do_nothing>>::value, ""); compiles happily.
I have struggled with libcxx's implementation of unique_ptr for long time, but still cannot figure out: how can std::unique_ptr be not assignable(! is_assignable) when std::unique_ptr provides move assignments?
p1 and p2 are of a different type. Unlike with shared_ptr, the deleter of a unique_ptr is part of the pointer's type. This means the move assignment operator does not allow you to assign (even move-assign) between two unique_ptrs if their deleter types differ.
unique_ptr also offers an assignment operator template which allows assigning from an rvalue of unique_ptr with a different deleter, but the deleters must be assignable (see reference). So you can make your static assert fire by making the deleters assignable:
struct do_nothing
{
template <class T>
void operator()(T*) {}
template <class T>
operator std::default_delete<T>() { return {}; }
};
int
main()
{
int i = 0;
std::unique_ptr<int, do_nothing> p1(&i);
std::unique_ptr<int> p2;
static_assert(!std::is_assignable<decltype(p2), decltype(p1)>::value, ""); // note ! here.
}
[Live example]
After some investigation, looks like the behavior initializing variables follows some convention.
For single element:
auto* x = new int; // undefined value
auto* x = new int{}; // 0 (default initializer)
auto* x = new int(23); // 23 (copy initializer)
auto* x = new Test; // default constructor
auto* x = new Test{}; // default constructor
auto* x = new Test(...); // X constructor (overload-chosen)
This makes a lot of sense, until you try to apply the same logic on arrays:
auto* x = new int[10]; // all undefined values - OK
auto* x = new int[10]{}; // all 0 (default initializer) - OK
auto* x = new int[10](23); // all 23 (copy initializer on all) - NOT IMPLEMENTED
auto* x = new Test[10]; // default constructors - OK
auto* x = new Test[10]{}; // default constructors - OK
auto* x = new Test[10](...); // X constructor on all (overload-chosen) - NOT IMPLEMENTED
My logic says that you can make some assumptions:
If a type can be constructed using T(Args..) then every element from the array can be constructed. There is no reason to not allow T[N](Args...) syntax.
Does anyone know why this feature doesn't exist? It would be very nice to allow
new int[10](23); // all 23
new Test[10]("asd", 123); // construct all using Test("asd", 123)
Edit: The whole idea of this is to avoid the default initialization/constructor and call the one we need directly.
While it would be nice if there was something for this in the standard library†, the functionality you want is relatively straightforward to implement:
namespace detail {
template<typename T, typename... ArgTs, std::size_t... Is>
constexpr auto make_filled_array(ArgTs const&... args, std::index_sequence<Is...>)
-> std::array<T, sizeof...(Is)> {
return {{(Is, T(args...))...}};
}
}
template<typename T, std::size_t N, typename... ArgTs, std::enable_if_t<N != 0, int> = 0>
constexpr auto make_filled_array(ArgTs const&... args) {
return detail::make_filled_array<T, ArgTs...>(args..., std::make_index_sequence<N>{});
}
// ...
auto arr1 = make_filled_array<int, 10>(23);
auto arr2 = make_filled_array<Test, 10>("asd", 123);
Online Demo
That said, I don't see any point to taking constructor arguments here; container emplace functions are useful because they do perfect-forwarding, but we can't do that here because we need to reuse the arguments for each constructor call (so moving from them is not an option). I think copy-construction is natural:
namespace detail {
template<typename T, std::size_t... Is>
constexpr auto make_filled_array(T const& t, std::index_sequence<Is...>)
-> std::array<T, sizeof...(Is)> {
return {{(Is, t)...}};
}
}
template<std::size_t N, typename T, std::enable_if_t<N != 0, int> = 0>
constexpr auto make_filled_array(T const& t) {
return detail::make_filled_array(t, std::make_index_sequence<N>{});
}
// ...
auto arr1 = make_filled_array<10>(23);
auto arr2 = make_filled_array<10>(Test{"asd", 123});
Online Demo
N.b. your use of new implies that you may be from a managed language and need to read up on value semantics. ;-] This code could be altered to return a std::array<T, N>* or T* (or even std::unique_ptr<T[]> for something moderately reasonable), but, why would one..? Just use std::vector<T>:
std::vector<int> vec(10, 23);
I chose to demonstrate std::array<> here because your examples all have a constant size.
† The closest thing that comes to mind is std::fill, but that wouldn't initialize the array...
I can't speak for the committee, but I can see a case where your syntax would be very very problematic: initializing from temporaries: .
X* x = new X[10](Y{});
How do you deal with this case?
You can't call X(Y&&) (assuming it exists) for each of the 10 elements because you have only 1 temporary and all elements after the first would be initialized from an object in an unspecified state.
The other alternative would be to somehow call X(const Y&) instead (assuming again it exists). Besides over complicating the standard and adding unexpected inconsistent behavior that just opens up another can of worms. The X object could have a reference bind to our Y{} assuming is an lvalue and you end up with a dangling reference.
The only viable solution I see is to not allow rvalue references on this syntax but that would just feed more SO questions.
I think it is pretty safe to assume the standard committee took this into consideration.
Is there a convenient way to re-assign the value of a unique_ptr with a new owned object, without re-specifying the type?
For instance:
std::unique_ptr<int> foo;
// .... Later, once we actually have a value to store...
foo = std::make_unique<int>(my_cool_value);
Of course int is not too much of an eyesore, but foo::element_type could be long or subject to change after a refactoring.
So, to use type inference, we could do:
foo = std::make_unique<decltype(foo)::element_type>(value);
...But that's pretty hideous (foo::element_type doesn't work because foo can't be used in a constant expression).
Ideally, std::unique_ptr would support a forwarding emplace-like method:
foo.reassign(value);
This would release the old value and, just like std::vector::emplace, construct the new owned object in-place.
....But as far as I can tell, there's nothing more concise than make_unique<decltype(foo)::element_type>.
EDIT: The most concise way to reassign the value for a type that supports operator= is, of course, to use operator=:
*foo = value;`
...But I do not want to rely on the copyability of element_type (for instance, I initially ran into this issue when trying to work with input-file streams).
Stash the arguments (or references thereto) into a proxy object with a templated conversion operator that deduces the target type. Then construct the new object once you have that deduced.
template<class... Args>
struct maker {
template<class T>
operator std::unique_ptr<T>() && {
return make<T>(std::index_sequence_for<Args...>());
}
std::tuple<Args...> args;
private:
template<class T, size_t ... Is>
std::unique_ptr<T> make(std::index_sequence<Is...>) {
return std::make_unique<T>(std::get<Is>(std::move(args))...);
}
};
template<class... Args>
auto maybe_make_unique_eventually(Args&&... args){
return maker<Args&&...>{std::forward_as_tuple(std::forward<Args>(args)...)};
}
It won't be a member function, but a free function could essentially achieve this:
template<typename T, typename D, typename...Args>
void TakeNew(std::unique_ptr<T,D>& up, Args&&... args)
{
up.reset(new T{std::forward<Args>(args)...});
// or use parentheses for consistency with `make_unique`; see comments
}
// usage...
auto foo = std::make_unique<int>(3);
// .... Later...
TakeNew(foo, 5);
(I do not consider this solution ideal.)
#include <memory>
// a class with a long and unweildy name
namespace mary {
namespace poppins {
struct supercalafragalisticexpialadocious
{
};
}
}
int main()
{
// what we don't want to have to do:
auto a = std::make_unique<mary::poppins::supercalafragalisticexpialadocious>();
// so alias the typename
using atrocious = mary::poppins::supercalafragalisticexpialadocious;
// same type with a shorter name
a = std::make_unique<atrocious>();
}
As you have unique ownership, unless the type is not copyable, you may simply do
*foo = value;
Given the class template
template<class T>
struct A {
A( const T & x );
};
I would like to instantiate objects of this class without writing out the actual type T, because this is typically a clumsy type resulting from some sort of expression templates and lambda functions. One way to accomplish this would be
template<class T>
A<T> create_A( const T& t ){
return A<T>( t );
}
int main(){
auto a = create_A( complicated_expression );
}
Here I never wrote the actual type of the expression, but this creates a copy of A and won't work without a copy constructor. I don't have a copy (or move) constructor. What I'm looking for is something like
A a( complicated_expression );
Clean and simple, and the compiler should be able to figure out the actual type. Unfortunately this isn't valid C++. So what would be the best valid C++ syntax to accomplish the same thing? Currently I'm doing this:
auto x = complicated_expression;
A<decltype(x)> a(x);
But this seems unnecessary verbose. Is there a better way to do this?
The code
template<class T>
A<T> create_A( const T& t ){
return A<T>( t );
}
int main(){
auto a = create_A( complicated_expression );
}
formally requires copy or move constructor to be defined and available, but any reasonable compiler will be able to optimize it away using copy elision rule. It is the standard idiom used widely across the standard library (e.g. std::make_pair).
Well, if your compiler is MSVC, this will work:
template<class T>
struct A {
A(const T & x) {}
A(const A&) = delete;
A(A&&) = delete;
};
template <typename T>
A<T> create_A(const T& t)
{
return t;
}
const auto& a = create_A(666);
const auto& b = create_A(a);
const auto& c = create_A(b);
No such luck with clang and g++, though.
Assigning a result returned by value to a const reference is perfectly legal, by the way, and has its uses. Why MSCV avoids checking for type being moveable/copyable (even though it would optimise it away) is a mystery to me and likely a bug. But, it'd kinda work in your case if you need to do it this way.
EDIT: alternatively, if you are not afraid to bring the wrath of C++ gods upon yourself, you can transform create_A into a macro:
#define create_A(x) (A<decltype(x)>(x))
Now it shall work on all compilers.
EDIT2: as #dyp suggested, this answer can be improved further:
template <typename T>
A<T> create_A(const T& t)
{
return { t };
}
auto&& a = create_A(666);
auto&& b = create_A(a);
auto&& c = create_A(b);
It will work on all C++11 compilers.
The technique you're trying to implement is called template argument deduction.
So, I don't think you can achive something like:
A a( complicated_expression );
Since here A is the template's name and the template argument deduction takes place only in functions. Not in classes. With the line above you will not be able to avoid the missing template arguments error.