Basically the example says it all: assigning to a std::chrono literal - does not give an error message.
I tried on gcc, clang and vc, (c++17) all these compilers accept it.
Is this intended behaviour?
#include <chrono>
using namespace std::chrono_literals;
int main()
{
10ms += 1ms; // would expect error here
12ms = 10ms; // and here
// 3 = 4; // like here
return 0;
}
are std::chrono literals lvalues?
No. std::chrono_literals are prvalues.
Is this intended behaviour?
It is as the standard has specified.
These values are of class types. And rvalues of class types can be assigned1 as long as the class is assignable, and their assignment operator overload is not lvalue ref qualified. Assignment operators of classes, like all member functions, are not lvalue ref qualified by default, and hardly any standard class has such qualified overload2 - I don't know of any but if there are, std::chrono::duration isn't one of them.
Note that the literal isn't modified. The left hand operand is a temporary object which in this case is discarded.
Another simple example of assigning an rvalue:
std::string{"temporary"} = "this is pointless, but legal";
1 This is actually a useful feature, although there aren't many use cases. Here is a useful example:
std::vector<bool> vec{true, false, true};
vec[1] = true; // vec[1] is an rvalue
2 Prior to C++11, there were no ref qualifiers, and thus all rvalues of assignable class types could be assigned. To maintain backward compatibility, the default cannot be changed, nor can pre-existing classes be changed (at least not without a deprecation period). std::chrono::duration was added in C++11, so it could have had qualified assignment operator, but standard authors appear to prefer not qualifying their assignment operators.
The std::chrono literals are not lvalues. They may appear as such, but what you are seeing is actually invocation of assignment operators by r-values. Why is this allowed? Specifically, because e.g. the member assignment operator
constexpr duration& operator+=(const duration& d)
as specified by [time.duration.arithmetic]/9, does not use any ref-qualifiers, meaning it does not reject being invoked by r-values.
Compare with the following example (implemented as duration above)
struct Foo {
unsigned long long i;
constexpr Foo& operator+=(const Foo& d) { return *this; }
};
constexpr Foo operator"" _Foo (unsigned long long i) {
return Foo{i};
}
int main() {
1_Foo += 2_Foo; // OK!
}
as compared to the following example, which explicitly makes use of a & ref-qualifier to implicitly ban invocation on r-values:
struct Foo {
unsigned long long i;
constexpr Foo& operator+=(const Foo& d) & { return *this; }
};
constexpr Foo operator"" _Foo (unsigned long long i) {
return Foo{i};
}
int main() {
1_Foo += 2_Foo; // error: no viable overloaded '+='
}
or, the following example, which explicitly deletes the r-value overload:
struct Foo {
unsigned long long i;
constexpr Foo& operator+=(const Foo& d) & { return *this; }
constexpr Foo& operator+=(const Foo& d) && = delete;
};
constexpr Foo operator"" _Foo (unsigned long long i) {
return Foo{i};
}
int main() {
1_Foo += 2_Foo; // error: overload resolution selected deleted operator '+='
}
For details regarding r-value qualifiers and how they could make sense to apply in order to ban to-rvalue-assignment, see e.g. the following Q&A:
What does the & (ampersand) at the end of member function signature mean?
Related
Consider the following code :
template <class T>
T average(T *atArray, int nNumValues)
{
T tSum = 0;
for (int nCount = 0; nCount < nNumValues; nCount++)
{
tSum += atArray[nCount];
}
tSum = tSum / nNumValues;
return tSum;
}
And the following question on it :
Which of the following statements about the class/type T must be true in order for the code to compile and run without crashing?
It must be some kind of numeric type
Must have < operator-defined
Must have the [ ] access operator-defined
Must have copy constructor and assignment operator
My Thoughts:
I think it could be something apart from numeric type but it will need to have the operator + and / well defined.
point 2 seems incorrect as < has no relation with / and + operator
same with point 3 and point 4.
Although I am not sure about my reasoning above.
No - It could for example do implicit conversions to/from a numeric type and have operator+= defined.
No - That operator is not used on a T
No - That operator is not used on a T, only a T* and pointer types all have this operator defined.
No - The copy constructor can be deleted and the question says "and" so the answer is no. It needs some kind of assignment operator though.
Consider this class, especially constructed to be able to answer no to as many questions as possible:
struct foo {
foo() = default;
foo(int) {} // implicit conversion from int
foo(const foo&) = delete; // no copy constructor
foo(foo&&) = default; // but move constructor
foo& operator=(const foo&) = delete; // no copy assignment
foo& operator=(foo&&) = delete; // no move assignment
foo& operator=(int) { return *this; }; // but assignment from int
foo& operator+=(const foo&) { return *this; } // for tSum += atArray[nCount];
operator int() const { return 1; } // implicit conversion to int
};
And it would work fine with the function template:
int main() {
foo arr[2];
auto x = average(arr, 2);
}
A class type with sufficient overloaded operators (like std::complex<double>) could work.
The < is only used on int expressions, not involving T.
The atArray[nCount] expression uses a T* pointer, not an expression with type T, so it has the built-in meaning.
Technically no, since fundamental types don't have constructors or assignment operators at all, just similar semantics. Also, technically every class type "has" a copy constructor and assignment operator, even if deleted. Another issue: T could be a class with normal assignment operator, deleted copy constructor, and public non-deleted move constructor.
Probably the intended answer is #4.
The actual requirements on T are:
A variable of type T can be copy-initialized from an int, or from a null pointer constant. (0 is a special expression which can do two different things...)
T is a numeric type, or an enumeration or class type with valid operator functions supporting expressions:
a += b and a = b, where a and b are lvalues of type T
a / n, where a is an lvalue of type T and n is an lvalue of type int
T is move-constructible.
Given a simple class template with multiple implicit conversion functions (non-explicit constructor and conversion operator), as in the following example:
template<class T>
class Foo
{
private:
T m_value;
public:
Foo();
Foo(const T& value):
m_value(value)
{
}
operator T() const {
return m_value;
}
bool operator==(const Foo<T>& other) const {
return m_value == other.m_value;
}
};
struct Bar
{
bool m;
bool operator==(const Bar& other) const {
return false;
}
};
int main(int argc, char *argv[])
{
Foo<bool> a (true);
bool b = false;
if(a == b) {
// This is ambiguous
}
Foo<int> c (1);
int d = 2;
if(c == d) {
// This is ambiguous
}
Foo<Bar> e (Bar{true});
Bar f = {false};
if(e == f) {
// This is not ambiguous. Why?
}
}
The comparison operators involving primitive types (bool, int) are ambiguous, as expected - the compiler does not know whether it should use the conversion operator to convert the left-hand template class instance to a primitive type or use the conversion constructor to convert the right-hand primitive type to the expected class template instance.
However, the last comparison, involving a simple struct, is not ambiguous. Why? Which conversion function will be used?
Tested with compiler msvc 15.9.7.
According to [over.binary]/1
Thus, for any binary operator #, x#y can be interpreted
as either x.operator#(y) or operator#(x,y).
According to this rule, in the case of e == f, the compiler can only interpret it as e.operator==(f), not as f.operator==(e). So there is no ambiguity; the operator== you defined as a member of Bar is simply not a candidate for overload resolution.
In the case of a == b and c == d, the built-in candidate operator==(int, int) (see [over.built]/13) competes with the operator== defined as a member of Foo<T>.
Operator overloads implemented as member functions don't allow for implicit conversion of their left-hand operand, which is the object on which they are called.
It always helps to write out the explicit call of an operator overload to better understand exactly what it does:
Foo<Bar> e (Bar{true});
Bar f = {false};
// Pretty explicit: call the member function Foo<Bar>::operator==
if(e.operator ==(f)) { /* ... */ }
This can't be confused with the comparison operator in Bar, because it would require an implicit conversion of the left-hand side, which is impossible.
You can trigger an ambiguity similar to the ones you see with the built-in types when you define Bar and its comparison operator like this:
struct Bar { bool m; };
// A free function allows conversion, this will be ambiguous:
bool operator==(const Bar&, const Bar&)
{
return false;
}
This is nicely demonstrated and explained in Scott Meyers's Effective C++, Item 24.
In the class below,
Why would you make the operators explicit. I thought that explicit was to prevent implicit calling of constructors?
class Content
{
public:
virtual ~Content() = 0;
virtual explicit operator float&();
virtual explicit operator long long&();
virtual explicit operator std::string&()
}
I thought that explicit was to prevent implicit calling of
constructors?
Since C++11 it also applies to user-defined conversions (a.k.a. the cast operator).
Why would you make the operators explicit
Used in this context, the explicit keyword makes the conversion eligible only for direct-initialization and explicit conversions. See here under [class.conv.fct¶2]:
A conversion function may be explicit ([dcl.fct.spec]), in which case
it is only considered as a user-defined conversion for
direct-initialization ([dcl.init]). Otherwise, user-defined
conversions are not restricted to use in assignments and
initializations.
This aids you in making sure the compiler doesn't try the conversion against your intention, so that you have to explicitly cast it yourself, leaving less room for error. Example:
struct Foo
{
explicit operator int() {return 0;}
operator int*() {return nullptr;}
};
int main()
{
Foo foo;
//int xi = foo; // Error, conversion must be explicit
int i = static_cast<int>(foo); // OK, conversion is explicit
int* i_ptr = foo; // OK, implicit conversion to `int*` is allowed
int i_direct(foo); // OK, direct initialization allowed
int* i_ptr_direct(foo); // OK, direct initialization after implicit conversion
return 0;
}
It can also help resolve ambiguity in cases where multiple conversion options apply, leaving the compiler without a criteria for deciding which one to choose:
struct Bar
{
operator int() {return 1;}
operator char() {return '1';}
};
int main()
{
Bar bar;
//double d = bar; // Error, implicit conversion is ambiguous
return 0;
}
Add explicit:
struct Bar
{
operator int() {return 1;}
explicit operator char() {return '1';}
};
int main()
{
Bar bar;
double d = bar; // OK, implicit conversion to `int` is the only option
return 0;
}
Consider the following:
struct Content
{
operator float() { return 42.f; }
friend Content operator+(Content& lhs, float) { return lhs; }
};
int main()
{
Content c{};
c + 0; // error: ambiguous overload for 'operator+'
}
Here, the compiler cannot choose between operator+(Content&, float) and operator+(float, int). Making the float operator explicit resolves this ambiguity*:
c + 0; // operator+(Content&, float)
or
static_cast<float>(c) + 0; // operator+(float, int)
*) provided it makes sense to prefer one over the other.
The other answers cover how it works, but I think you should be told why it was added to C++.
A smart pointer usually has a conversion to bool so you can do this:
std::shared_ptr<int> foo;
if (foo) {
*foo = 7;
}
where if(foo) converts foo to bool. Unfortunately:
int x = foo+2;
converts foo to bool, then to int, then adds 2. This is almost always a bug. This is permitted because while only one user defined conversion is done, a user defined conversion followed by a built in conversion can silently occur.
To fix this programmers would do crazy things like add:
struct secret {
void unused();
};
struct smart_ptr {
using secret_mem_ptr = void(secret::*)();
operator secret_mem_ptr() const { return 0; }
};
and secret_mem_ptr is a secret pointer to member. A pointer to member has a built in conversion to bool, so:
smart_ptr ptr;
if (!ptr) {
}
"works" -- ptr is convert to secret_mem_ptr, which then is convered to bool, which is then used to decide which branch to take.
This was more than a bit of a hack.
They added explicit on conversion operators to solve this exact problem.
Now:
struct smart_ptr {
explicit operator bool() const { return true; }
};
doesn't permit:
smart_ptr ptr;
int x = 3 + ptr;
but it does permit:
if (ptr) {
}
because the rules were hand-crafted to support exactly that use case. It also doesn't permit:
bool test() {
smart_ptr ptr;
return ptr;
}
here, you have to type:
bool test() {
smart_ptr ptr;
return (bool)ptr;
}
where you explicitly convert ptr to bool.
(I am usually really against C-style casts; I make an exception in the case of (bool)).
You would use it if you wanted a Content object never to be implicitly converted to (say) a float. This could happen in the following way:
void f( float f );
....
Content c;
f( c ); // conversion from Content to float
Without the explicit qualifier, the conversion happens implicitly; with it, you get a compilation error.
Silent, implicit conversions can be the source of a lot of confusion and/or bugs, so it's generally better to make the operators explicit , or probably better yet to provide named functions, such as ToFloat, which tell the reader exactly what is going on.
struct B
{
};
struct A
{
operator A&() const;
operator B&() const;
};
int main()
{
const A a;
B& br = a;
A& ar = a;
}
Why can I create cast operator to B&, but not to A&.
May be it does not have much sense (one can use it to erase const modifier, as in example), but it at least inconsistent!
You can't do this because it's explicitly forbidden. N3290 § 12.3.2 states:
Such functions are called
conversion functions. No return type can be specified. If a conversion function is a member function, the
type of the conversion function (8.3.5) is “function taking no parameter returning conversion-type-id”. A
conversion function is never used to convert a (possibly cv-qualified) object to the (possibly cv-qualified)
same object type (or a reference to it), to a (possibly cv-qualified) base class of that type (or a reference to
it), or to (possibly cv-qualified) void.
(Emphasis mine)
This is discussed further in a note:
These conversions are considered as standard conversions for the purposes of overload resolution (13.3.3.1, 13.3.3.1.4) and
therefore initialization (8.5) and explicit casts (5.2.9).
Which explains this decision - it would interfere with the built-in mechanics too much. (For little gain).
If you really want something non-const from a const object the only smart way to do this is constructing a new instance using the copy constructor.
As a work around you could introduce a lightweight intermediary (like a smart pointer):
struct B {};
struct A {};
namespace {
B b_inst;
A a_inst;
}
struct A_wrapper {
A& inst;
// This is perfectly fine: const alters the reference, not what it refers to
operator A&() const { return inst; }
operator B&() const { return b_inst; }
A_wrapper() : inst(a_inst) {}
};
int main() {
const A_wrapper a;
B& br = a;
A& ar = a;
}
But really, wanting to do this in the first place looks like a code smell.
The proper way to do this would be to use const_cast.
For example,
#include <iostream>
using namespace std;
void f(int* p) {
cout << *p << endl;
}
int main(void) {
const int a = 10;
const int* b = &a;
// Function f() expects int*, not const int*
// f(b);
int* c = const_cast<int*>(b);
f(c);
// Lvalue is const
// *b = 20;
// Undefined behavior
// *c = 30;
int a1 = 40;
const int* b1 = &a1;
int* c1 = const_cast<int*>(b1);
// Integer a1, the object referred to by c1, has
// not been declared const
*c1 = 50;
return 0;
}
Declaring a conversion to a reference to self is not ill-formed. Your problem comes at the time where your reference is initialized. As the type of the reference and the type of the initialization expression are the same, the reference is bound directly and your user defined conversion operator is never considered. Thus normal conversion rules apply and const conversion makes the code ill-formed.
Anyway, what your are doing is basically asking yourself to get shot in the foot. If you don't like constness, don't use it. If you do it consistently, it will never bother you, but it is not going to make you new friends.
Please, could someone explain in plain English what is "Extending move semantics to *this"? I am referring to this proposal. All what am looking for is what is that & why do we need that. Note that I do understand what an rvalue reference is in general, upon which move semantics is built. I am not able to grasp what such an extension adds to rvalue references!
The ref-qualifier feature (indicating the type of *this) would allow you to distinguish whether a member function can be called on rvalues or lvalues (or both), and to overload functions based on that. The first version gives some rationale in the informal part:
Prevent surprises:
struct S {
S* operator &() &; // Selected for lvalues only
S& operator=(S const&) &; // Selected for lvalues only
};
int main() {
S* p = &S(); // Error!
S() = S(); // Error!
}
Enable move semantics:
class X {
std::vector<char> data_;
public:
// ...
std::vector<char> const & data() const & { return data_; }
std::vector<char> && data() && { return data_; } //should probably be std::move(data_)
};
X f();
// ...
X x;
std::vector<char> a = x.data(); // copy
std::vector<char> b = f().data(); // move
For example, you can overload operators as free functions with rvalue references if you wish:
Foo operator+(Foo&& a, const Foo& b)
{
a += b;
return std::move(a);
}
To achieve the same effect with a member function, you need the quoted proposal:
Foo Foo::operator+(const Foo& b) && // note the double ampersand
{
*this += b;
return *this;
}
The double ampersand says "this member function can only be called on rvalues".
Whether or not you must explicitly move from *this in such a member function is discussed here.