How to guarantee copy elision with std::variant? - c++

I have this type:
struct immobile {
// other stuff omitted
immobile(immobile&) = delete;
immobile(immobile&&) = delete;
};
immobile mk_immobile();
// e.g. this compiles
// mk_immobile() is a prvalue and i is its result object
immobile i(mk_immobile());
I also have this class template:
template<typename T>
struct container {
std::variant<T, other_stuff> var;
template<typename... Args>
container(Args&&... args)
: var(std::in_place_index<0>, std::forward<Args>(args)...) {}
};
I want to construct a container around the object produced by mk_immobile(), with the immobile object used to initialize one of the variants of var.
container<immobile> c(mk_immobile());
However, this does not work. For one, std::variant's constructor wants std::is_constructible_v<immobile, immobile>, which doesn't hold. Worse, even this simplified version fails:
template<typename T>
struct demonstration {
T t;
template<typename... Args>
demonstration(Args&&... args) : t(std::forward<Args>(args)...) {}
};
demonstration<immobile> d(mk_immobile());
Which seems to imply that std::forward does not, in fact, perfectly forward—prvalues do not forward as prvalues. (This makes sense to me; I don't think doing that would be possible.) I can make demonstration work by changing it to this:
template<typename T>
struct demonstration {
T t;
template<typename F>
demonstration(F&& f) : t(std::forward<F>(f)()) {}
};
demonstration<immobile> d([] { return mk_immobile(); });
But I do not see a way to change container in a similar manner. How do I change container so that it can construct a std::variant (or other tagged union) out of a prvalue? I can change container but cannot change immobile.

You abuse casts
template<typename F>
struct initializer
{
F f;
template<typename T>
operator T()
{
return f();
}
};
template<typename F>
initializer(F&&) -> initializer<F>;
And use as
container<immobile> c{initializer{[]{
return mk_immobile();
}}};

Related

Compiler infering the template argument

template<typename T>
class A
{
public:
A(T &t)
: t_(t){}
T t_;
};
int main()
{
int value;
A<decltype(value)> a(value);
// what I wish for : A a(value);
// which does not compile "missing template argument before 'a'"
}
Is there a way in the declaration of A (or somewhere else) to hint the compiler that T should be automatically resolved to the type passed to the constructor ?
(ideally c++11, but happy to hear about less old versions)
C++17 does it out of the box (or with the help of deduction guides), previous versions cannot.
As answered by #Quentin, this is only possible starting in C++17. However, if you're fine with calling a function to create your A objects, the following should do what you want in C++11:
template <class T, class NonRefT = typename std::remove_reference<T>::type>
A<NonRefT> create_A (T && t) {
return A<NonRefT>(std::forward<T>(t));
}
// Or even closer to your original code:
template <class T>
auto create_A (T && t) -> A<decltype(t)> {
return A<decltype(t)>(std::forward<T>(t));
}
I used std::remove_reference based on your use of decltype, though you might want to use std::decay instead.
int main () {
int value = 5;
auto a = create_A(value);
}
If I remember correctly the example code has an edge-case where it does not work as expected pre-C++17. The compiler will elide the copy/move constructor to create a from the rvalue returned by create_A(). However, it will check during compilation whether A's copy/move constructor (which it won't use) is available/accessible. Starting from C++17 the copy/move elision is done "properly" and no copy/move constructor is necessary for such code. (Also, I might be misremembering and it might be checking for copy/move assignment instead.)
In C++11 you can create a simple make_A function like this:
#include <iostream>
template <typename T>
class A {
public:
A(T &t) : t_(t) {}
T t_;
};
template <typename T>
A<T> make_A(T&& t) {
return A<T>(std::forward<T>(t));
}
int main() {
int value = 0;
auto a = make_A(value);
return 0;
}
Demo

how to check if type 'T' has 'T(std::initializer_list<U>)' constructor

I have a function func <T> (...) which must be separated into two branches;
1st branch: case when type T have T(std::initializer_list<U>) constructor.
2nd branch: case when type T does not have T(std::initializer_list<U>) constructor.
My current implementation is as follows:
template<typename T, typename U>
using has_init_list_ctor = std::enable_if_t<std::is_constructible_v<
T,
std::initializer_list<U>
>>;
// version for T with initialization list ctor
template<
typename T,
typename = std::enable_if_t<
std::is_detected_v<has_init_list_ctor, T, /* idk how to auto-deduce type U */>
>
>
void func() {
//...
}
// version for T without initialization list ctor
template<
typename T,
typename = std::enable_if_t<
!std::is_detected_v<has_init_list_ctor, T, /* idk how to auto-deduce type U */>
>
>
void func() {
//...
}
But it have a flaw. I have no idea how to auto-deduce type U from T.
the ideal solution would be:
template<typename T>
struct deduce_U_from_T
{
// implementation.
usign type = /* ??? */;
};
template<typename T>
using has_init_list_ctor = std::enable_if_t<std::is_constructible_v<
T,
std::initializer_list<
typename deduce_U_from_T<T>::type
>
>>;
but i have no idea how to implement deduce_U_from_T.
Is there any way to solve this problem?
or is there any workaround?
Update:
Function func <T> (...) is an imitation of std::alocator_traits::construct().
I am trying to implement my own "allocator" for the use of std::vector and smart pointers. Under normal circumstances, I would use the default std::alocator_traits, but this time, I need to request memory from the "special" pool (it is something implemented by me, it can be called "virtual heap", it is accessed via methods like T * get_memory <T> (...), the pool performs additional operations during mem allocation, and offers different "modes" of allocation - I am sorry for being very generic but currently it is WIP, and it constantly changes)
simplistic implementation of func <T> (...) (allocator_traits::construct())
template<typename T>
class allocator_traits
{
//...
public:
template<typename... Args>
static
std::enable_if_t<
std::is_detected_v<has_init_list_ctor, T>,
void
> construct(T * ptr, Args && ... args)
{
new(ptr) T(std::forward<Args>(args)...); // normal brackets // construct with placment-new
}
template<typename... Args>
static
std::enable_if_t<
!std::is_detected_v<has_init_list_ctor, T>,
void
> construct(T * ptr, Args && ... args)
{
new(ptr) T{ std::forward<Args>(args)... }; // curly brackets // construct with placment-new
}
//...
};
The difference lies in the ability to construct type T with curly brackets (when type T doesn't have T(std::initializer_list<U>) constructor.
Is there any way to solve this problem?
Yes. Don't solve it. You shouldn't be trying to guess what kind of initialization the user wants. Just do this:
new (ptr) T(std::forward<Args>(args)...)
If the user wants to use an initializer_list constructor, they can pass in an instance of an initializer_list and that'll work just fine.
The more interesting case is aggregates, which is why they will be initializable with parentheses in C++20 (see P0960). But that can be worked around by passing in an argument which has an appropriate conversion operator. That is, if I want to construct a:
struct X { int i; };
and make that work with parentheses, I can pass in an argument of type:
struct X_factory { int i; operator X() const { return X{i}; } };
and with guaranteed copy elision, we get the right effect anyway.
In any case, initializer_list actually isn't strictly related to the question. What you probably would have wanted (and I wouldn't suggest doing this) is:
if constexpr (std::is_constructible_v<T, Args...>) {
new (ptr) T(std::forward<Args>(args)...);
} else {
new (ptr) T{std::forward<Args>(args)...};
}
Or possibly in the reverse order writing a trait for direct-list-initializable.
#Barry is right, this is a bad idea.
Here is how to do it:
#include <initializer_list>
#include <utility>
#include <iostream>
struct any_il{
template<class U>
operator std::initializer_list<U>()const{ return {}; }
};
struct foo {
foo(std::initializer_list<int>){}
foo(foo&&)=default;
};
template<class T, class=void>
struct can_from_il:std::false_type{};
template<class T>
struct can_from_il<T, decltype(void( T(any_il{}) ) )>:std::true_type{};
int main(){
std::cout << can_from_il<foo>{}() << can_from_il<int>{}() <<"\n";
}
It has many flaws.

std::move or std::forward when assigning universal constructor to member variable in C++

Consider the following classes foo1 and foo2
template <typename T>
struct foo1
{
T t_;
foo1(T&& t) :
t_{ std::move(t) }
{
}
};
template <typename T>
struct foo2
{
foo1<T> t_;
foo2(T&& t) :
t_{ std::forward<T>(t) }
{
}
};
Is it always the case that the constructor of foo1 represents the correct way to initialise the member variable T? i.e. by using std::move.
Is it always the case that the constructor of foo2 represents the correct way to initialise the member variable foo1<T> due to needing to forward to foo1's constructor? i.e. by using std::forward.
Update
The following example fails for foo1 using std::move:
template <typename T>
foo1<T> make_foo1(T&& t)
{
return{ std::forward<T>(t) };
}
struct bah {};
int main()
{
bah b;
make_foo1(b); // compiler error as std::move cannot be used on reference
return EXIT_SUCCESS;
}
Which is a problem as I want T to be both a reference type and a value type.
Neither of these examples use universal references (forwarding references, as they are now called).
Forwarding references are only formed in the presence of type deduction, but T&& in the constructors for foo1 and foo2 is not deduced, so it's just an rvalue reference.
Since both are rvalue references, you should use std::move on both.
If you want to use forwarding references, you should make the constructors have a deduced template argument:
template <typename T>
struct foo1
{
T t_;
template <typename U>
foo1(U&& u) :
t_{ std::forward<U>(u) }
{
}
};
template <typename T>
struct foo2
{
foo1<T> t_;
template <typename U>
foo2(U&& u) :
t_{ std::forward<U>(u) }
{
}
};
You should not use std::move in foo1 in this case, as client code could pass an lvalue and have the object invalidated silently:
std::vector<int> v {0,1,2};
foo1<std::vector<int>> foo = v;
std::cout << v[2]; //yay, undefined behaviour
A simpler approach would be to take by value and unconditionally std::move into the storage:
template <typename T>
struct foo1
{
T t_;
foo1(T t) :
t_{ std::move(t) }
{
}
};
template <typename T>
struct foo2
{
foo1<T> t_;
foo2(T t) :
t_{ std::move(t) }
{
}
};
For the perfect forwarding version:
Passed lvalue -> one copy
Passed rvalue -> one move
For the pass by value and move version:
Passed lvalue -> one copy, one move
Passed rvalue -> two moves
Consider how performant this code needs to be and how much it'll need to be changed and maintained, and choose an option based on that.
This depends on how you deduce T. For example:
template<class T>
foo1<T> make_foo1( T&& t ) {
return std::forward<T>(t);
}
In this case, the T in foo1<T> is a forwarding reference, and your code won't compile.
std::vector<int> bob{1,2,3};
auto foo = make_foo1(bob);
the above code silently moved from bob into a std::vector<int>& within the constructor to foo1<std::vector<int>&>.
Doing the same with foo2 would work. You'd get a foo2<std::vector<int>&>, and it would hold a reference to bob.
When you write a template, you must consider what it means for the type T to be reference. If your code doesn't support it being a reference, consider static_assert or SFINAE to block that case.
template <typename T>
struct foo1 {
static_assert(!std::is_reference<T>{});
T t_;
foo1(T&& t) :
t_{ std::move(t) }
{
}
};
Now this code generates a reasonable error message.
You might think the existing error message was ok, but it was only ok because we moved into a T.
template <typename T>
struct foo1 {
static_assert(!std::is_reference<T>{});
foo1(T&& t)
{
auto internal_t = std::move(t);
}
};
here only the static_assert ensured that our T&& was actual an rvalue.
But enough with this theoretical list of problems. You have a concrete one.
In the end this is probably want you want:
template <class T> // typename is too many letters
struct foo1 {
static_assert(!std::is_reference<T>{});
T t_;
template<class U,
class dU=std::decay_t<U>, // or remove ref and cv
// SFINAE guard required for all reasonable 1-argument forwarding
// reference constructors:
std::enable_if_t<
!std::is_same<dU, foo1>{} && // does not apply to `foo1` itself
std::is_convertible<U, T> // fail early, instead of in body
,int> = 0
>
foo1(U&& u):
t_(std::forward<U>(u))
{}
// explicitly default special member functions:
foo1()=default;
foo1(foo1 const&)=default;
foo1(foo1 &&)=default;
foo1& operator=(foo1 const&)=default;
foo1& operator=(foo1 &&)=default;
};
or, the simpler case that is just as good in 99/100 cases:
template <class T>
struct foo1 {
static_assert(!std::is_reference<T>{});
T t_;
foo1(T t) :
t_{ std::move(t) }
{}
// default special member functions, just because I never ever
// want to have to memorize the rules that makes them not exist
// or exist based on what other code I have written:
foo1()=default;
foo1(foo1 const&)=default;
foo1(foo1 &&)=default;
foo1& operator=(foo1 const&)=default;
foo1& operator=(foo1 &&)=default;
};
As a general rule, this simpler technique results in exactly 1 move more than the perfect forwarding technique, in exchange for a huge amount less code and complexity. And it permits {} initialization of the T t argument to your constructor, which is nice.

Concept checking a function doesn't work with movable-only arguments

I have a function that calls a callback function that accepts a movable-only type (for example unique_ptr).
template <typename Function>
void foo(const Function& function) {
BOOST_CONCEPT_ASSERT((
boost::UnaryFunction<Function, void, std::unique_ptr<Bar>));
auto bar = std::make_unique<Bar>();
...
function(std::move(bar));
}
Trying to compile this code, I get a message that the BOOST_CONCEPT_ASSERT line tries to copy the unique_ptr. If I remove the line, the code works fine. It seems that the Boost.Concept library does not support move semantics. Is there any workaround for this without writing my own concept class (which, incidentally, would not be very simple to support both lvalues and rvalues as their arguments).
That's correct. Unfortunately, UnaryFunction as a concept is written as:
BOOST_concept(UnaryFunction,(Func)(Return)(Arg))
{
BOOST_CONCEPT_USAGE(UnaryFunction) { test(is_void<Return>()); }
private:
void test(boost::mpl::false_)
{
f(arg); // "priming the pump" this way keeps msvc6 happy (ICE)
Return r = f(arg);
ignore_unused_variable_warning(r);
}
void test(boost::mpl::true_)
{
f(arg); // <== would have to have std::move(arg)
// here to work, or at least some kind of
// check against copy-constructibility, etc.
}
#if (BOOST_WORKAROUND(__GNUC__, BOOST_TESTED_AT(4) \
&& BOOST_WORKAROUND(__GNUC__, > 3)))
// Declare a dummy construktor to make gcc happy.
// It seems the compiler can not generate a sensible constructor when this is instantiated with a refence type.
// (warning: non-static reference "const double& boost::UnaryFunction<YourClassHere>::arg"
// in class without a constructor [-Wuninitialized])
UnaryFunction();
#endif
Func f;
Arg arg;
};
Since arg is passed by lvalue, there's no way to get that to work with Boost.Concepts. Directly. You could write a hack though. Since we're just calling checking that f(arg) is valid, we could construct a local type for arg that is convertible to unique_ptr<Bar>. That is:
template <typename Function>
void foo(Function f)
{
struct Foo {
operator std::unique_ptr<int>();
};
BOOST_CONCEPT_ASSERT((
boost::UnaryFunction<Function, void, Foo>));
f(std::make_unique<int>(42));
}
Or more generally:
template <typename T>
struct AsRvalue {
operator T(); // no definition necessary
};
template <typename Function>
void foo(Function f)
{
BOOST_CONCEPT_ASSERT((
boost::UnaryFunction<Function, void, AsRvalue<std::unique_ptr<int>>>));
f(std::make_unique<int>(42));
}
That compiles for me on gcc and clang (though gives a warning on clang about unused typedefs). However, at that point, it may be clearer to just write out your own concept to get it to work. Something like Piotr's would be easiest.
#include <type_traits>
#include <utility>
template <typename...>
struct voider { using type = void; };
template <typename... Ts>
using void_t = typename voider<Ts...>::type;
template <typename, typename = void_t<>>
struct is_callable : std::false_type {};
template <typename F, typename... Args>
struct is_callable<F(Args...), void_t<decltype(std::declval<F>()(std::declval<Args>()...))>> : std::true_type {};
//...
static_assert(is_callable<Function&(std::unique_ptr<Bar>)>{}, "Not callable");
DEMO

How to make my uninitialised_allocator safe?

Following from this question, I want to use an unitialised_allocator with, say, std::vector to avoid default initialisation of elements upon construction (or resize() of the std::vector (see also here for a use case). My current design looks like this:
// based on a design by Jared Hoberock
template<typename T, typename base_allocator >
struct uninitialised_allocator : base_allocator::template rebind<T>::other
{
// added by Walter Q: IS THIS THE CORRECT CONDITION?
static_assert(std::is_trivially_default_constructible<T>::value,
"value type must be default constructible");
// added by Walter Q: IS THIS THE CORRECT CONDITION?
static_assert(std::is_trivially_destructible<T>::value,
"value type must be default destructible");
using base_t = typename base_allocator::template rebind<T>::other;
template<typename U>
struct rebind
{
typedef uninitialised_allocator<U, base_allocator> other;
};
typename base_t::pointer allocate(typename base_t::size_type n)
{
return base_t::allocate(n);
}
// catch default construction
void construct(T*)
{
// no-op
}
// forward everything else with at least one argument to the base
template<typename Arg1, typename... Args>
void construct(T* p, Arg1 &&arg1, Args&&... args)default_
{
base_t::construct(p, std::forward<Arg1>(arg1), std::forward<Args>(args)...);
}
};
Then an unitialised_vector<> template could be defined like this:
template<typename T, typename base_allocator = std::allocator<T>>
using uninitialised_vector =
std::vector<T,uninitialised_allocator<T,base_allocator>>;
However, as indicated by my comments, I'm not 100% certain as to what are the appropriate conditions in the static_assert()? (Btw, one may consider SFINAE instead -- any useful comments on this are welcome)
Obviously, one has to avoid the disaster that would ensue from the attempted non-trivial destruction of an uninitialised object. Consider
unitialised_vector< std::vector<int> > x(10); // dangerous.
It was suggested (comment by Evgeny Panasyuk) that I assert trivial constructibility, but this does not seem to catch the above disaster scenario. I just tried to check what clang says about std::is_trivially_default_constructible<std::vector<int>> (or std::is_trivially_destructible<std::vector<int>>) but all I got was a crash of clang 3.2 ...
Another, more advanced, option would be to design an allocator which only elides the default construction for objects for which this would be safe to do so.
Fwiw, I think the design can be simplified, assuming a C++11 conforming container:
template <class T>
class no_init_allocator
{
public:
typedef T value_type;
no_init_allocator() noexcept {}
template <class U>
no_init_allocator(const no_init_allocator<U>&) noexcept {}
T* allocate(std::size_t n)
{return static_cast<T*>(::operator new(n * sizeof(T)));}
void deallocate(T* p, std::size_t) noexcept
{::operator delete(static_cast<void*>(p));}
template <class U>
void construct(U*) noexcept
{
static_assert(std::is_trivially_default_constructible<U>::value,
"This allocator can only be used with trivally default constructible types");
}
template <class U, class A0, class... Args>
void construct(U* up, A0&& a0, Args&&... args) noexcept
{
::new(up) U(std::forward<A0>(a0), std::forward<Args>(args)...);
}
};
I see little advantage to deriving from another allocator.
Now you can let allocator_traits handle rebind.
Template the construct members on U. This helps if you want to use this allocator with some container that needs to allocate something other than a T (e.g. std::list).
Move the static_assert test into the single construct member where it is important.
You can still create a using:
template <class T>
using uninitialised_vector = std::vector<T, no_init_allocator<T>>;
And this still fails to compile:
unitialised_vector< std::vector<int> > x(10);
test.cpp:447:17: error: static_assert failed "This allocator can only be used with trivally default constructible types"
static_assert(std::is_trivially_default_constructible<U>::value,
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
I think the test for is_trivially_destructible is overkill, unless you also optimize destroy to do nothing. But I see no motivation in doing that since I believe it should get optimized anyway whenever appropriate. Without such a restriction you can:
class A
{
int data_;
public:
A() = default;
A(int d) : data_(d) {}
};
int main()
{
uninitialised_vector<A> v(10);
}
And it just works. But if you make ~A() non trivial:
~A() {std::cout << "~A(" << data_ << ")\n";}
Then, at least on my system, you get an error on construction:
test.cpp:447:17: error: static_assert failed "This allocator can only be used with trivally default constructible types"
static_assert(std::is_trivially_default_constructible<U>::value,
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
I.e. A is no longer trivially constructible if it has a non-trivial destructor.
However even with the non-trivial destructor you can still:
uninitialised_vector<A> v;
v.push_back(A());
This works, only because I didn't overreach with requiring a trivial destructor. And when executing this I get ~A() to run as expected:
~A(0)
~A(0)