C++17 make_optional constexpr-ness - c++

This page says that the make_optional function in C++17 returns a constexpr optional<...>. I think (I might be wrong though) this would require that optional<T> has a constexpr copy or move constructor. However, this page also says that's not the case.
I don't know how make_optional can be implemented as the C++1z draft currently stands. See this post for clarification. Is there some workaround, or maybe it's just the standard draft/cppreference's mistake?

Thanks to #Yakk and #T.C. for their explanations. I feel an example should make things clearer:
struct wrapper {
int value;
// non-explicit constexpr constructor
constexpr wrapper(int v) noexcept : value(v) {}
// non-constexpr copy & move constructors
wrapper(const wrapper& that) noexcept : value(that.value) {}
wrapper(wrapper&& that) noexcept : value(that.value) {}
};
constexpr wrapper make_wrapper(int v)
{
return {v};
}
int main()
{
constexpr auto x = make_wrapper(123); // error! copy/move construction,
// but no constexpr copy/move ctor
constexpr int y = make_wrapper(123).value; // ok
static_assert(y == 123, ""); // passed
}
So make_wrapper does successfully return a constexpr wrapper; it's the copy/move construction (although usually elided by compilers) that prevents the code from compiling, since there is no constexpr copy/move constructor.
We can verify the constexpr-ness of the returned (temporary) wrapper object by using its member value to initialize a constexpr variable.

You can directly construct return values in C++11 with return {something}; If there are any non-explicit ctors that are constexpr you can return it from a function.

Related

User defined conversion and template member question

I'm using the following example from Stroustrup C++ 4th Ed Page 764-765. I'm a bit confused on it's behavior and looking for guidance.
First, why doesn't Ptr<X> xp2 = py; compile, when there is a user defined conversion template function? FYI, I added this to the example.
Second, why does Ptr<X> xp{py}; invoke the user defined conversion operator, then when I step through in gdb, the copy constructor Ptr(const Ptr& r) is not invoked? Maybe something is being invoked implicitly?
template<typename T> class Ptr
{
T* p;
public:
Ptr(T* t) : p{t} {}
Ptr(const Ptr& r) {p = r.p;}
template<typename T2>
explicit operator Ptr<T2>();
};
template<class T>
template<class T2>
Ptr<T>::operator Ptr<T2>()
{
return Ptr<T2>{p};
}
class X {};
class Y : public X {};
int main(int argc, char *argv[])
{
Y y;
Ptr<Y> py{&y};
Ptr<X> xp{py};
//Ptr<X> xp2 = py; // no candidate, thought this would work
Ptr<X> x2{xp};
return 0;
}
First, why doesn't Ptr<X> xp2 = py; compile, when there is a user defined conversion template function? FYI, I added this to the example.
This requests implicit conversion to Ptr<X> but there is no implicit conversion operator. The conversion operator is declared explicit so it is not a candidate.
Second, why does Ptr<X> xp{py}; invoke the user defined conversion operator, then when I step through in gdb, the copy constructor Ptr(const Ptr& r) is not invoked?
The compiler is almost certainly eliding the copy. Try compiling with -fno-elide-constructors and then see if the copy constructor is invoked.
Note that copy/move elision is codified in C++17 by temporary materialization rules. In this case there isn't a copy/move at all (no such constructor is even required to exist) so if you are compiling in C++17 mode then even -fno-elide-constructors will not cause the copy constructor to be invoked.

constexpr if and the return value optimization

I have this code:
#include <string>
class A {
public:
// A(A const &) = delete; // Code fails if this is uncommented.
explicit A(int);
explicit A(::std::string const &);
private:
::std::string myname_;
int foo_;
};
static constexpr bool which = false;
A test(::std::string const &s, int a)
{
if constexpr (which) {
A x{a};
return x;
} else {
A y{s};
return y;
}
}
This code fails if A has a deleted copy constructor. But, given the rules about return type for functions with if constexpr in them, it seems like the compiler should be applying RVO here.
Is there a reason why it isn't, other than it being an overlooked case in the language specification?
This has nothing to do with if constexpr
Simply this code is not allowed to compile:
class A {
public:
A(A const &) = delete;
explicit A(int);
};
A test(int a)
{
A x{a};
return x; // <-- error call to a deleted constructor `A(A const &) = delete;`
}
The changes in C++17 you are thinking of have to do with temporary materialization and don't apply to NRVO because x is not a prvalue.
For instance this code was illegal pre C++17 and now it is allowed:
A test(int a)
{
return A{a}; // legal since C++17
}
This is NRVO, which is non-mandatory copy elision:
(emphasis mine)
In a return statement, when the operand is the name of a non-volatile object with automatic storage duration, which isn't a
function parameter or a catch clause parameter, and which is of the
same class type (ignoring cv-qualification) as the function return
type. This variant of copy elision is known as NRVO, "named return
value optimization".
This is an optimization: even when it takes place and the copy/move
(since C++11) constructor is not called, it still must be present and
accessible (as if no optimization happened at all), otherwise the
program is ill-formed:
BTW: Note that in your code, both the if part and else part of the constexpr if statement will be checked.
Outside a template, a discarded statement is fully checked. if
constexpr is not a substitute for the #if preprocessing directive:
void f() {
if constexpr(false) {
int i = 0;
int *p = i; // Error even though in discarded statement
}
}

Assignment vs constructor in C++ [duplicate]

I wrote some code S s; ... s = {};, expecting it to end up the same as S s = {};. However it didn't. The following example reproduces the problem:
#include <iostream>
struct S
{
S(): a(5) { }
S(int t): a(t) {}
S &operator=(int t) { a = t; return *this; }
S &operator=(S const &t) = default;
int a;
};
int main()
{
S s = {};
S t;
t = {};
std::cout << s.a << '\n';
std::cout << t.a << '\n';
}
The output is:
5
0
My questions are:
Why is operator=(int) selected here, instead of "ambiguous" or the other one?
Is there a tidy workaround, without changing S?
My intent is s = S{}; . Writing s = {}; would be convenient if it worked. I'm currently using s = decltype(s){}; however I'd prefer to avoid repeating the type or the variable name.
Why is operator=(int) selected here, instead of "ambiguous" or the other one?
{} to int is the identity conversion ([over.ics.list]/9). {} to S is a user-defined conversion ([over.ics.list]/6) (technically, it's {} to const S&, and goes through [over.ics.list]/8 and [over.ics.ref] first before coming back to [over.ics.list]/6).
The first wins.
Is there a tidy workaround?
A variation of the trick std::experimental::optional pulls to make t = {} always make t empty.
The key is to make operator=(int) a template. If you want to accept int and only int, then it becomes
template<class Int, std::enable_if_t<std::is_same<Int, int>{}, int> = 0>
S& operator=(Int t) { a = t; return *this; }
Different constraints can be used if you want to enable conversions (you'd probably also want to take the argument by reference in that case).
The point is that by making the right operand's type a template parameter, you block t = {} from using this overload - because {} is a non-deduced context.
...without changing S?
Does template<class T> T default_constructed_instance_of(const T&) { return {}; } and then s = default_constructed_instance_of(s);count?
First of all, the case has nothing to do with the "int" version of the assignment operator, you can just delete it. You can actually delete the other assignment operator too as it will be generated by the compiler. IE this kind of type automatically receives copy/move constructors and the assignment operator. (ie they are not prohibited and you are just repeating what the compiler does automatically with explicit notation)
The first case
uses copy initialization:
S s = {}; // the default constructor is invoked
That is a post-construction copy assignment, yet compilers optimize such simple cases. You should use direction initialization instead:
S s{}; // the default constructor is invoked (as you have it)
Note, you can also write:
S s; // the default constructor is invoked if you have it
The second case
What you should write is direct initialization of the right hand side of the copy assignment:
t = S{};
This notation will invoke the default constructor (if there is one), or value initialization for the members (as long as the type is an aggregate). Here is the relevant info: http://en.cppreference.com/w/cpp/language/value_initialization

Does a constexpr move constructor ever make sense?

Does it ever make sense to have a constexpr move constructor?
For example, consider the following:
#include <array>
class C
{
public:
constexpr C(std::array<int, 3> ar) : m_ar{ar} {}
constexpr C(C&& other) : m_ar{std::move(other.m_ar)} { }
private:
std::array<int, 3> m_ar;
};
int main()
{
constexpr C c1 {{{1, 2, 3}}};
constexpr C c2{std::move(c1)};
return 0;
}
This doesn't compile, since despite calling std::move on c1, the compiler deduces it needs to use the (implicitly deleted) copy constructor, not the move constructor. I'm not sure why.
But if I remove the constexpr from c1, then it becomes unusable by the constexpr move constructor.
Is there any way to get this to work? Or is this a bad example for a constexpr move constructor, but there are good examples? Or, is it just always wrong to have a constexpr move constructor?
In principle, a move constructor can be used with a non-const object whose lifetime started during the evaluation of the constant expression:
// C++14
constexpr int f() {
C a(/* ... */);
C b = std::move(a);
return 0;
}
constexpr int i = f();
Similar things can be done in C++11, e.g.
constexpr C foo(C&& c) {
return std::move(c);
}
constexpr int f() {
return foo(C()), 0;
}
That said, since everything used in a constant expression is required to be trivially destructible, the usefulness of a move constructor is rather limited.
This doesn't compile, since despite calling std::move on c1, the compiler deduces it needs to use the (implicitly deleted) copy constructor
c1 is of type C const. When you move() it, that's really a cast to rvalue reference, so you get a C const&&. Note that it's still const. When we perform overload resolution, there are three constructors:
C(std::array<int, 3> ); // not viable
C(C&& ); // not viable
C(C const& ) = delete; // viable!
C const&& can't bind to C&& for the same reason that C const& can't bind to C&. We're left with just the copy constructor, which is implicitly deleted.
Having a constexpr move constructor could make sense - but the object you're "moving" from can't really be moved from, because it's presumably const. You could add a const move constructor:
constexpr C(C const&& other) : m_ar(other.m_ar) { }
This is a glorified copy constructor, but it allows the syntax you want. May as well just allow copying.

Can explicitly defaulted / deleted functions overload on ref qualifiers?

INTRO
Ref qualifiers : A way to dissambiguate the rl-valuness of the implied object. As a quick example, take the following class
class example
{
int member;
public:
// ...
int& value() &;
// ^
int&& value() &&;
// ^^
int const& value() const&;
// ^
};
The use of this C++11 feature (syntax marked with ^), allows us to control the version of value() that will be called with
l-values
temporaries
const l-values
Practically the ref qualification applies to the classe's *this
Defaulted / Deleted functions : Specify a special member function as having the compiler generated (default) definition or inaccessible (delete). As an example take
struct type {
type(const type&) = delete;
type& operator=(const type&) = delete;
};
The above struct, achieves being non copyable with extremely clear semantics
QUESTIONs
Is it possible / valid to combine these features ?
Which are the cases where it's explicitly forbidden or bad style ?
Is there any use case / pattern for such a combination ? (Eg creating conditional interfaces based rl-valueness quick and easy)
Yes, but there's not much use, as constructors and destructors can't be ref-qualified.
You can ref-qualify assignment operators:
struct S {
S &operator=(const S &) && = default;
};
int main() {
S s;
S() = s; // OK
s = S(); // error - LHS must be an rvalue
}
However, I'm somewhat at a loss to imagine what this would be useful for.