Should implicitly generated assignment operators be & ref-qualified? - c++

The following code compiles without problem on gcc 4.8.1:
#include <utility>
struct foo
{
};
int main()
{
foo bar;
foo() = bar;
foo() = std::move( bar );
}
It seems the implicitly generated assignment operators for foo are not & ref-qualified and so can be invoked on rvalues. Is this correct according to the standard? If so, what reason is there for not requiring implicitly generated assignment operators to be & ref-qualified?
Why doesn't the standard require the following to be generated?
struct foo
{
foo & operator=( foo const & ) &;
foo & operator=( foo && ) &;
};

Well, there are certain legitimate use cases for assigning to an rvalue. To quote from Ref-qualifiers for assignment operators in the Standard Library:
There are only a few very specific types for which it makes sense to
support assigning to an rvalue. In particular, types that serve as a
proxy, e.g., vector<bool>::reference, and types whose assignment
operators are const-qualified (e.g., slice_array).
The C++ standard committee obviously felt that default assignment should not have an implicit ref qualifier - rather it should be explicitly declared. Indeed, there may be existing code which would stop working if all of a sudden all implicitly declared assignment operators didn't work with rvalues.
Granted, it's a bit hard to contrive an example where we want an implicitly declared assignment operator to work with rvalues, but the C++ standard committee likely doesn't want to take these kind of chances when it comes to preserving backwards compatibility. Code like this:
int foo_counter = 0;
struct Foo
{
Foo()
{
++foo_counter;
}
~Foo()
{
--foo_counter;
}
};
int main()
{
Foo() = Foo();
}
...wouldn't work anymore. And at the end of the day, the standards committee wants to make sure that previously valid C++ (no matter how stupid or contrived) continues to work in C++11.

It seems like your question is more "Why would assignment to an rvalue ever be useful?" rather than "Why doesn't the standard ref-qualify auto generated constructors?"
The reason assignment to an rvalue is allowed is because there are some cases where it is useful.
One example usage is with std::tie (link):
#include <set>
#include <tuple>
int main()
{
std::set<int> s;
std::set<int>::iterator iter;
bool inserted;
// unpacks the return value of insert into iter and inserted
std::tie(iter, inserted) = s.insert(7);
}
Example borrowed from cppreference.com then modified

Related

What can be done to prevent misleading assigment to returned value?

After many years of using C++ I realized a quirk in the syntax when using custom classes.
Despite being the correct language behavior it allows to create very misleading interfaces.
Example here:
class complex_arg {
double r_;
double phi_;
public:
std::complex<double> value() const {return r_*exp(phi_*std::complex<double>{0, 1});}
};
int main() {
complex_arg ca;
ca.value() = std::complex<double>(1000., 0.); // accepted by the compiler !?
assert( ca.value() != std::complex<double>(1000., 0.) ); // what !?
}
https://godbolt.org/z/Y5Pcjsc8d
What can be done to the class definition to prevent this behavior?
(Or at the least flag the user of the clas that the 3rd line is not really doing any assignment.)
I see only one way out, but it requires modifying the class and it doesn't scale well (to large classes that can be moved).
const std::complex<double> value() const;
I also tried [[nodiscard]] value() but it didn't help.
As a last resort, maybe something can be done to the returned type std::complex<double> ? (that is, assuming one is in control of that class)
Note that I understand that sometimes one might need to do (optimized) assign to a newly obtained value and passe it to yet another function f( ca.value() = bla ). I am not questioning this usage per se (although it is quite confusing as well); I have the problem mostly with ca.value() = bla; as a standalone statement that doesn't do what it looks.
Ordinarily we can call a member function on an object regardless of whether that object's value category is an lvalue or rvalue.
What can be done to the class definition to prevent this behavior?
Prior to modern C++ there was no way prevent this usage. But since C++11 we can ref-qualify a member function to do what you ask as shown below.
From member functions:
During overload resolution, non-static member function with a cv-qualifier sequence of class X is treated as follows:
no ref-qualifier: the implicit object parameter has type lvalue reference to cv-qualified X and is additionally allowed to bind rvalue implied object argument
lvalue ref-qualifier: the implicit object parameter has type lvalue reference to cv-qualified X
rvalue ref-qualifier: the implicit object parameter has type rvalue reference to cv-qualified X
This allows us to do what you ask for a custom managed class. In particular, we can & qualify the copy assignment operator.
struct C
{
C(int)
{
std::cout<<"converting ctor called"<<std::endl;
}
C(){
std::cout<<"default ctor called"<<std::endl;
}
C(const C&)
{
std::cout<<"copy ctor called"<<std::endl;
}
//-------------------------v------>added this ref-qualifier
C& operator=(const C&) &
{
std::cout<<"copy assignment operator called"<<std::endl;;
return *this;
}
};
C func()
{
C temp;
return temp;
}
int main()
{
//---------v---------> won't work because assignment operator is & qualified
func() = 4;
}

Why is a cast operator to std::optional ignored?

This code
#include <iostream>
#include <optional>
struct foo
{
explicit operator std::optional<int>() {
return std::optional<int>( 1 );
}
explicit operator int() {
return 2;
}
};
int main()
{
foo my_foo;
std::optional<int> my_opt( my_foo );
std::cout << "constructor: " << my_opt.value() << std::endl;
my_opt = static_cast<std::optional<int>>(my_foo);
std::cout << "static_cast: " << my_opt.value() << std::endl;
}
produces the following output
constructor: 2
static_cast: 2
in Clang 4.0.0 and in MSVC 2017 (15.3). (Let's ignore GCC for now, since it's behavior seems to be buggy in that case.)
Why is the output 2? I would expect 1. The constructors of std::optional seem to prefer casting to the inner type (int) despite the fact that a cast to the outer type (std::optional<int>) is available. Is this correct according to the C++ standard? If so, is there a reason the standard does not dictate to prefer an attempt to cast to the outer type? I would find this more reasonable and could imagine it to be implemented using enable_if and is_convertible to disable the ctor if a conversion to the outer type is possible. Otherwise every cast operator to std::optional<T> in a user class - even though it is a perfect match - would be ignored on principle if there is also one to T. I would find this quite obnoxious.
I posted a somewhat similar question yesterday but probably did not state my problem accurately, since the resulting discussion was more about the GCC bug. That's why I am asking again more explicitly here.
In case Barry's excellent answer still isn't clear, here's my version, hope it helps.
The biggest question is why isn't the user-defined conversion to optional<int> preferred in direct initialization:
std::optional<int> my_opt(my_foo);
After all, there is a constructor optional<int>(optional<int>&&) and a user-defined conversion of my_foo to optional<int>.
The reason is the template<typename U> optional(U&&) constructor template, which is supposed to activate when T (int) is constructible from U and U is neither std::in_place_t nor optional<T>, and direct-initialize T from it. And so it does, stamping out optional(foo&).
The final generated optional<int> looks something like:
class optional<int> {
. . .
int value_;
. . .
optional(optional&& rhs);
optional(foo& rhs) : value_(rhs) {}
. . .
optional(optional&&) requires a user-defined conversion whereas optional(foo&) is an exact match for my_foo. So it wins, and direct-initializes int from my_foo. Only at this point is operator int() selected as a better match to initialize an int. The result thus becomes 2.
2) In case of my_opt = static_cast<std::optional<int>>(my_foo), although it sounds like "initialize my_opt as-if it was std::optional<int>", it actually means "create a temporary std::optional<int> from my_foo and move-assign from that" as described in [expr.static.cast]/4:
If T is a reference type, the effect is the same as performing the
declaration and initializationT t(e); for some invented temporary
variable t ([dcl.init]) and then using the temporary variable as the
result of the conversion. Otherwise, the result object is
direct-initialized from e.
So it becomes:
my_opt = std::optional<int>(my_foo);
And we're back to the previous situation; my_opt is subsequently initialized from a temporary optional, already holding a 2.
The issue of overloading on forwarding references is well-known. Scott Myers in his book Effective Modern C++ in Chapter 26 talks extensively about why it is a bad idea to overload on "universal references". Such templates will tirelessly stamp out whatever the type you throw at them, which will overshadow everything and anything that is not an exact match. So I'm surprised the committee chose this route.
As to the reason why it is like this, in the proposal N3793 and in the standard until Nov 15, 2016 it was indeed
optional(const T& v);
optional(T&& v);
But then as part of LWG defect 2451 it got changed to
template <class U = T> optional(U&& v);
With the following rationale:
Code such as the following is currently ill-formed (thanks to STL for
the compelling example):
optional<string> opt_str = "meow";
This is because it would require two user-defined conversions (from
const char* to string, and from string to optional<string>) where the
language permits only one. This is likely to be a surprise and an
inconvenience for users.
optional<T> should be implicitly convertible from any U that is
implicitly convertible to T. This can be implemented as a non-explicit
constructor template optional(U&&), which is enabled via SFINAE only
if is_convertible_v<U, T> and is_constructible_v<T, U>, plus any
additional conditions needed to avoid ambiguity with other
constructors...
In the end I think it's OK that T is ranked higher than optional<T>, after all it's a rather unusual choice between something that may have a value and the value.
Performance-wise it is also beneficial to initialize from T rather than from another optional<T>. An optional is typically implemented as:
template<typename T>
struct optional {
union
{
char dummy;
T value;
};
bool has_value;
};
So initializing it from optional<T>& would look something like
optional<T>::optional(const optional<T>& rhs) {
has_value = rhs.has_value;
if (has_value) {
value = rhs.value;
}
}
Whereas initializing from T& would require less steps:
optional<T>::optional(const T& t) {
value = t;
has_value = true;
}
A static_cast is valid if there is an implicit conversion sequence from the expression to the desired type, and the resulting object is direct-initialized from the expression. So writing:
my_opt = static_cast<std::optional<int>>(my_foo);
Follows the same steps as doing:
std::optional<int> __tmp(my_foo); // direct-initialize the resulting
// object from the expression
my_opt = std::move(__tmp); // the result of the cast is a prvalue, so move
And once we get to construction, we follow the same steps as my previous answer, enumerating the constructors, which ends up selecting the constructor template, which uses operator int().

The "most important const" vs. auto_ptr: Why the code does not compile?

The following code does not compile on Visual C++ 2008 nor 2010:
#include <memory>
struct A {};
std::auto_ptr<A> foo() { return std::auto_ptr<A>(new A); }
const std::auto_ptr<A> bar() { return std::auto_ptr<A>(new A); }
int main()
{
const std::auto_ptr<A> & a = foo(); // most important const
const std::auto_ptr<A> & b = bar(); // error C2558:
// class 'std::auto_ptr<_Ty>' :
// no copy constructor available or copy
// constructor is declared 'explicit'
bar(); // No error?
}
I expected the "most important const" to apply to the variable "b", and yet, it does not compile, and for some reason, the compiler asks for a copy constructor (which surprises me as there should be no copy involved here). The standalone call to bar() works fine, which means, I guess, it is really the initialization of b that is the problem.
Is this a compiler bug, or a genuine compilation error described in the standard?
(perhaps it was forbidden in C++98 and authorized in C++11?)
Note: It does compile on Visual C++ 2012, gcc 4.6, and on Solaris CC (of all compilers...), but not gcc 3.4, nor XL C)
In C++03 and C++98, when binding a const reference to an rvalue (such as a function returning by value), the implementation may bind the reference directly to the rvalue or it may make a copy of the rvalue and bind the reference to that copy. As auto_ptr's copy constructor takes a non-const reference, this second choice will only work if the rvalue returned is not const qualified but the compiler is still allowed to attempt this, even if it won't work.
In C++11, these extra copies are not allowed and the implementation must bind directly to the rvalue if a conversion isn't required.
See also here.
Pre C++11, at least, the standard required an object to be
copyable in this context. In the end, the semantics of:
T const& t = f();
, where f returns a T by value, is:
T tmp = f();
T const& t = tmp;
Which requires a copy constructor.
In the case of std::auto_ptr, the problem that you're seeing
is that the copy constructor is defined to take a non-const
reference, which means that you cannot copy a temporary. Some
compilers (e.g. Microsoft) don't enforce this, which means that
your code may work with them, but it is fundamentally illegal.
The real question is why you are using references here. You
need a local variable one way or the other; the reference only
introduces an additional layer of indirection.

Should I still return const objects in C++11? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Should I return const objects?
(The original title of that question was: int foo() or const int foo()? explaining why I missed it.)
Effective C++, Item 3: Use const whenever possible. In particular, returning const objects is promoted to avoid unintended assignment like if (a*b = c) {. I find it a little paranoid, nevertheless I have been following this advice.
It seems to me that returning const objects can degrade performance in C++11.
#include <iostream>
using namespace std;
class C {
public:
C() : v(nullptr) { }
C& operator=(const C& other) {
cout << "copy" << endl;
// copy contents of v[]
return *this;
}
C& operator=(C&& other) {
cout << "move" << endl;
v = other.v, other.v = nullptr;
return *this;
}
private:
int* v;
};
const C const_is_returned() { return C(); }
C nonconst_is_returned() { return C(); }
int main(int argc, char* argv[]) {
C c;
c = const_is_returned();
c = nonconst_is_returned();
return 0;
}
This prints:
copy
move
Do I implement the move assignment correctly? Or I simply shouldn't return const objects anymore in C++11?
Returning const objects is a workaround that might cause other problems. Since C++11, there is a better solution for the assignment issue: Reference Qualifiers for member functions. I try to explain it with some code:
int foo(); // function declaration
foo() = 42; // ERROR
The assignment in the second line results in a compile-time error for the builtin type int in both C and C++. Same for other builtin types. That's because the assignment operator for builtin types requires a non-const lvalue-reference on the left hand side. To put it in code, the assignment operator might look as follows (invalid code):
int& operator=(int& lhs, const int& rhs);
It was always possible in C++ to restrict parameters to lvalue references. However, that wasn't possible until C++11 for the implicit first parameter of member functions (*this).
That changed with C++11: Similar to const qualifiers for member functions, there are now reference qualifiers for member functions. The following code shows the usage on the copy and move operators (note the & after the parameter list):
struct Int
{
Int(const Int& rhs) = default;
Int(Int&& rhs) noexcept = default;
~Int() noexcept = default;
auto operator=(const Int& rhs) & -> Int& = default;
auto operator=(Int&& rhs) & noexcept -> Int& = default;
};
With this class declaration, the assignment expression in the following code fragment is invalid, whereas assigning to a local variable works - as it was in the first example.
Int bar();
Int baz();
bar() = baz(); // ERROR: no viable overloaded '='
So there is no need to return const objects. You can restrict the assigment operators to lvalue references, so that everything else still works as expected - in particular move operations.
See also:
What is "rvalue reference for *this"?
Returning a const object by value arguably was never a very good idea, even before C++11. The only effect it has is that it prevents the caller from calling non-const functions on the returned object – but that is not very relevant given that the caller received a copy of the object anyway.
While it is true that being returned a constant object prevents the caller from using it wrongly (e.g. mistakenly making an assignment instead of a comparison), it shouldn't be the responsibility of a function to decide how the caller can use the object returned (unless the returned object is a reference or pointer to structures the function owns). The function implementer cannot possibly know whether the object returned will be used in a comparison or for something else.
You are also right that in C++11 the problem is even graver, as returning a const effectively prevents move operations. (It does not prevent copy/move elision, though.)
Of course it is equally important to point out that const is still extremely useful (and using it no sign of paranoia) when the function returns a reference or a pointer.
The reason that your call to const_is_returned triggers copy constructor rather than move constructor is the fact that move must modify the object thus it can't be used on const objects. I tend to say that using const isn't advised in any case and should be subjected to a programmer judgement, otherwise you get the things you demonstrated. Good question.

Binding lvalue to rvalue reference -- g++ bug?

As an answer to another question I wanted to post the following code (that is, I wanted to post code based on this idea):
#include <iostream>
#include <utility> // std::is_same, std::enable_if
using namespace std;
template< class Type >
struct Boxed
{
Type value;
template< class Arg >
Boxed(
Arg const& v,
typename enable_if< is_same< Type, Arg >::value, Arg >::type* = 0
)
: value( v )
{
wcout << "Generic!" << endl;
}
Boxed( Type&& v ): value( move( v ) )
{
wcout << "Rvalue!" << endl;
}
};
void function( Boxed< int > v ) {}
int main()
{
int i = 5;
function( i ); //<- this is acceptable
char c = 'a';
function( c ); //<- I would NOT like this to compile
}
However, while MSVC 11.0 chokes at the last call, as it IHMO should, MinGW g++ 4.7.1 just accepts it, and invokes the constructor with rvalue reference formal argument.
It looks to me as if an lvalue is bound to an rvalue reference. A glib answer could be that the lvalue is converted to rvalue. But the question is, is this a compiler bug, and if it’s not, how does the Holy Standard permit this?
EDIT: I managed to reduce it all to the following pretty short example:
void foo( double&& ) {}
int main()
{
char ch = '!';
foo( ch );
}
Fails to compile with MSVC 11.0, does compile with MinGW 4.7.1, which is right?
I haven't check the spec but I guess char can be automatically cast to int. Since you cannot assign anything (it's r-value) the R-value to temporary variable of type int (to be more explicit to (int)c value) will be passed.
I discovered that N3290 (identical to C++11 standard) contains non-normative example of binding double&& to rvalue generated from int lvalue, and the updated wording in §8.5.3
“If T1 is reference-related to T2 and the reference is an rvalue reference,
the initializer expression shall not be an lvalue.”
The rules were reportedly designed to avoid inefficient extra copying. Although I fail to see how such copying could not be optimized away. Anyway, whether the rationale is reasonable or not – and it certainly doesn't seem as a reasonable effect! – the following code is allowed, and compiles with both MSVC 11 and MinGW g++ 4.7:
struct Foo {};
struct Bar { Bar( Foo ) {} };
void ugh( Bar&& ) {}
int main()
{
Foo o;
ugh( o );
}
So apparently MSVC 11 is wrong in not permitting the lvalue -> rvalue conversion.
EDIT: I learned that there is Defect Report about this issue, DR 1414. The Feb 2012 conclusion was that the current behavior specification is “correct”, presumably with respect to how well it reflects the intention. It is however reportedly still discussed in the committee, presumably with respect to the practicality of the intention.
Presumably you agree this is valid?
void foo( double ) {} // pass-by-value
int main()
{
char ch = '!';
foo( ch );
}
There's an implicit conversion from char to double, so the function is viable.
It's the same in the example in your edited question, there's an implicit conversion that produces a temporary (i.e. an rvalue) and the rvalue-reference argument binds to that temporary. You can make that conversion explicit if you prefer:
void foo( double&& ) {} // pass-by-reference
int main()
{
char ch = '!';
foo( double(ch) );
}
but that doesn't really change anything in this case. That would be necessary if double -> char could only be converted explicitly (e.g. for class types with explicit constructors or explicit conversion operators) but double to char is a valid implicit conversion.
The "an rvalue-reference cannot bind to an lvalue" rule you're thinking of refers to binding a T&& to a T lvalue, and that rule isn't broken because the double&& doesn't bind to the char, it binds to a temporary created by the implicit conversion.
That rule doesn't only exist to prevent unnecessary extra copying, but to fix a real safety problem that existed with the previous rules, see http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2008/n2812.html
Edit: It was asked whether this behaviour is desirable on the committee reflector (see DR 1414) and it was decided that yes, this behaviour is intended and is correct. One of the arguments used to reach that position was that this code is more efficient with the current rules:
std::vector<std::string> v;
v.push_back("text");
With the current rules a temporary std::string is created by an implicit conversion, then std::vector<T>::push_back(T&&) is called, and the temporary is moved into the vector. If that push_back overload Wasn't viable for the result of a conversion then the code above would call std::vector<T>::push_back(const T&) which would cause a copy. The current rules make this real-world use case more efficient. If the rules said rvalue-refs cannot bind to the result of implicit conversions you would have to change the code above to get the efficiency of a move:
v.push_back( std::string{"text"} );
IMHO it makes no sense to have to explicitly construct a std::string when that constructor is not explicit. I want consistent behaviour from explicit/implicit constructors, and I want the first push_back example to be more efficient.