Can anybody explain why this code compiles:
typedef struct longlong
{
unsigned long low;
long high;
}
longlong;
typedef longlong Foo;
struct FooStruct
{
private:
Foo bar;
public:
void SetBar(Foo m)
{
bar = m;
}
Foo GetBar()
{
return bar;
}
};
int main()
{
FooStruct f;
Foo m1 = { 1,1 };
Foo m2 = { 2,2 };
f.SetBar(m1);
f.GetBar() = m2; // Here I'd expect an error such as
// "error: lvalue required as left operand of assignment"
}
I expected the compilation to fail with error: lvalue required as left operand of assignment on line f.GetBar() = m2; because IMO f.GetBar() is not an l-value, but it compiles seemlessly and f.GetBar() = m2; is a NOP.
On the other hand if I replace the typedef longlong Foo; by typedef long Foo;, the line mentioned before won't compile and I get the expected error.
I came along this issue while refactoring some old code. The code in this question has no purpose other than to illustrate this issue.
This works because longlong is a class, and as such = is longlong::operator =, the implicitly-defined assignment operator. And you can call member functions on temporaries as long as they're not qualified with & (the default operator = is unqualified).
To restrict the assignment operator to lvalues, you can explicitly default it with an additional ref-qualifier:
struct longlong
{
longlong &operator = (longlong const &) & = default;
// ^
// ...
};
Using operators on objects of class type means to call a function (either a member function of the left-hand operand, or a free function taking the left-hand operand as first argument). This is known as operator overloading.
It's fine to call functions on rvalues so there is no compilation error.
When implementing the overloaded assignment operator, you can mark it so that it can not be called on an rvalue, but the designer of whatever class you are using chose not to do that.
f.GetBar() = m2; // Here I'd expect an error such as
// "error: lvalue required as left operand of assignment"
Your expectation is in error. This rule only applies to objects of built-in type.
[C++14: 3.10/5]: An lvalue for an object is necessary in order to modify the object except that an rvalue of class type can also be used to modify its referent under certain circumstances. [ Example: a member function called for an object (9.3) can modify the object. —end example ]
Recall that your = here is actually a call to operator=, which is a function.
Reasons for this behavior were described in other answers.
One way to avoid this behavior would be qualifying your return type with const:
struct FooStruct
{
...
const Foo GetBar() {return bar;}
};
You cannot assign values to const objects, so the compiler will complain.
Related
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;
}
When one assigns to a volatile int from a non-volatile int, the compiler. When one assigns from a volatile struct from a non-volatile struct of the same type, the compiler appears to be extremely unhappy.
Consider the following simple program.
struct Bar {
int a;
};
volatile int foo;
int foo2;
volatile Bar bar;
Bar bar2;
int main(){
foo = foo2;
bar = bar2;
}
When I try to compile this code, I get an error on the second line of main but not the first.
g++ Main.cc -o Main
Main.cc: In function ‘int main()’:
Main.cc:13:9: error: passing ‘volatile Bar’ as ‘this’ argument discards qualifiers [-fpermissive]
bar = bar2;
^
Main.cc:1:8: note: in call to ‘Bar& Bar::operator=(const Bar&)’
struct Bar {
It seems that the problem occurs because a volatile Bar is being passed to the left side of the assignment operator, although I am not sure why this is not a problem for int.
I looked at this answer which suggested the following fix.
struct Bar {
int a;
volatile Bar& operator= (const Bar& other) volatile {
*this = other;
}
};
Unfortunately, this resulted in the following two warnings.
g++ Main.cc -o Main
Main.cc: In member function ‘volatile Bar& Bar::operator=(const Bar&) volatile’:
Main.cc:4:21: warning: implicit dereference will not access object of type ‘volatile Bar’ in statement
*this = other;
^
Main.cc: In function ‘int main()’:
Main.cc:16:15: warning: implicit dereference will not access object of type ‘volatile Bar’ in statement
bar = bar2;
I then looked at this answer, which mentions that I should cast the reference to an rvalue, but I am not sure which reference to cast, and which cast syntax to use in this case.
What is the correct incantation to make the assignment on the line 2 of main behave exactly as line 1 of main does, without warnings or errors?
Your initial problem is because the implicit assignment operator has signature
Bar& operator=(const Bar& rhs);
... and that isn't callable for a volatile object. The warnings are because your updated function returns a volatile reference, but that reference is never used. GCC thinks that this might be a problem. The simplest way to fix this is to change the return type to void!
There is another problem: Your function will call itself in an infinite recursion. I suggest the following:
struct Bar {
int a;
Bar& operator=(const Bar&rhs) = default;
void operator=(const volatile Bar& rhs) volatile // Note void return.
{
// Caution: This const_cast removes the volatile from
// the reference. This may lose the point of the original
// volatile qualification.
//
// If this is a problem, use "a = rhs.a;" instead - but this
// obviously doesn't generalize so well.
const_cast<Bar&>(*this) = const_cast<const Bar&>(rhs);
}
};
volatile Bar vbar;
Bar bar;
int main(){
vbar = bar; // All four combinations work.
bar = vbar;
vbar = vbar;
bar = bar;
return 0;
}
This means you won't be able to chain assignment operators when using volatile structs. I assert this is not a huge loss.
Final aside: Why are you using volatile - it's not very useful for multi-threaded code (it is useful for memory mapped IO).
#martin provides a great description of why the problem occurs, but then does not provide a general solution that doesn't cast away the volatile.
If casting away the volatile is okay, then it should probably be done outside the function before the assignment. Hiding it within the copy constructor leaves more potential of unforseen bugs creeping in.
Adjusting the solution a bit, gives the following more general solution:
struct Bar {
int a;
Bar& operator=(const Bar&rhs) = default;
auto & operator=(const volatile Bar& rhs) volatile
{
// Solution 1: depends on struct definition and can't be copied to many
static_assert(sizeof(*this) == sizeof(a), "struct elements added or changed. update below");
a = rhs.a;
// Solution 2: very general solution that could be used if copying to many structs
// Do default byte-by-byte copy operation, but can't use memcpy for volatile
static_assert(is_trivially_copy_assignable_v<remove_cvref_t<decltype(*this)>>>, "Byte-by-byte copy may not be possible with this class");
std::copy_n(reinterpret_cast<const volatile std::byte*>(&rhs), sizeof(*this), reinterpret_cast<volatile std::byte*>(this));
// Support chaining of assignments
//return *this; // Okay, but returning a volatile will generate spurious warnings if that value is not read afterword
return rhs; // assumes that you want to just chain assignments and not do anything weird
}
};
This version of copy constructor works for all 4 combos of volatile/non-volatile source and destination, however, you could also split into different combinations in case you want to allow optimizations for each case.
Consider this snippet of C++ code:
struct Foo {
float value;
operator float& () {
return this->value;
}
};
int main() {
Foo foo;
foo=1.0f; //Doesn't compile, foo isn't implicitly converted to a float&
return 0;
}
Why doesn't this compile? Is there a specific reason this wasn't included in the C++ standard? Or an equivalent does indeed exist and I'm just using it wrong?
For pretty much all other operators, your conversion operator would do exactly what you want, and it would continue to do exactly what you want even if you add custom operators.
struct Foo {
float value;
operator float& () { return this->value; }
Foo &operator+=(Foo);
};
int main() {
Foo foo {};
foo+=1.0; // compiles and works
// equivalent to foo.operator float&()+=1.0;
}
However, = is special, the rules for = are different compared to most other operators. As identified by T.C.:
13.3.1.2 Operators in expressions [over.match.oper]
4 For the built-in assignment operators, conversions of the left operand are restricted as follows:
(4.1) -- no temporaries are introduced to hold the left operand, and
(4.2) -- no user-defined conversions are applied to the left operand to achieve a type match with the left-most parameter of a built-in candidate.
Together with the fact that any custom operator= is not allowed to be defined as a global function, this makes sure that foo=bar; where foo is a class type always means foo.operator=(bar);, nothing else.
The fact that this one operator is singled out doesn't explain the reason, but does make it quite clear that it's an intentional decision, and making sure that foo=bar; always means foo.operator=(bar);, nothing else, by itself already seems like a valid reason.
Implicit conversion is only done in the following cases:
Implicit conversions are performed whenever an expression of some type
T1 is used in context that does not accept that type, but accepts some
other type T2; in particular:
when the expression is used as the argument when calling a function that is declared with T2 as parameter;
when the expression is used as an operand with an operator that expects T2;
when initializing a new object of type T2, including return statement in a function returning T2;
when the expression is used in a switch statement (T2 is integral type);
when the expression is used in an if statement or a loop (T2 is bool).
None of those is the case here. Instead the compiler is trying to find a suitable operator= to work with a double. To have this compile you need to overload that operator ( you actually want a float as seen in the code ):
Foo& operator=(float other)
{
value = f;
return *this;
}
And change your assignment to foo = 1.0f;
Your conversion function would work with for example:
float f = foo;
What is the "operator int" function below? What does it do?
class INT
{
int a;
public:
INT(int ix = 0)
{
a = ix;
}
/* Starting here: */
operator int()
{
return a;
}
/* End */
INT operator ++(int)
{
return a++;
}
};
The bolded code is a conversion operator. (AKA cast operator)
It gives you a way to convert from your custom INT type to another type (in this case, int) without having to call a special conversion function explicitly.
For example, with the convert operator, this code will compile:
INT i(1234);
int i_2 = i; // this will implicitly call INT::operator int()
Without the convert operator, the above code won't compile, and you would have to do something else to go from an INT to an int, such as:
INT i(1234);
int i_2 = i.a; // this wont compile because a is private
operator int() is a conversion operator, which allows this class to be used in place of an int. If an object of this type is used in a place where an int (or other numerical type) is expected, then this code will be used to get a value of the correct type.
For example:
int i(1);
INT I(2); // Initialised with constructor; I.a == 2
i = I; // I is converted to an int using `operator int()`, returning 2.
First things first:
$12.3.1/1 - "A constructor declared
without the function-specifier
explicit specifies a conversion from
the types of its parameters to the
type of its class. Such a constructor
is called a converting constructor."
In your example, INT is a User Defined class that has a converting constructor from 'int'.
Therefore the following code is well-formed:
INT i(1024); // direct initialization syntax
This means that you can get an INT object from an integer. However what does one do, if the INT object has to be converted back to an integer? Transitivity?
One can say that the class INT can provide a member function to return the encapsulated integer member
int x = i.geta();
This however is not very intuitive and is not a standardized approach. Also it is not intuitive when it comes to how built-in types work in such situations.
int z = 0;
int y1 = z; // copy initialization or
int y2(z); // direct initialization
double d = (int )z; // explicit cast
Therefor the Standard allows for such standardization and intuitiveness of converting User Defined Types by saying:
$12.3/2 - "A member function of a
class X having no parameters with a
name of the form [...]
operator conversion-type-id
[...]specifies a conversion from X to the
type specified by the
conversion-type-id. 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”.
This makes all of the following well-formed and retains harmony with the way built-in types work is
int y1 = i; // copy initialization or
int y2(i); // direct initialization
double d = (int )i; // explicit cast
It looks like it make an INT class which behaves a little like the regular int, just that some other operators are not yet defined.
Is this a homework problem?
Seems like it's a question from a classroom, so I'll invite you to check the documentation on how to create a class.
class Foo
{
public
Foo() {} // Constructor
Foo operator++ {} // Operation ++ on foo like:
// Foo foo;
// foo++;
};
Please help me understand how exactly the conversion operators in C++ work.
I have a simple example here which I am trying to understand, though it is not very clear how the conversion actually happens by the compiler.
class Example{
public:
Example();
Example(int val);
operator unsigned int();
~Example(){}
private:
int itsVal;
};
Example::Example():itsVal(0){}
Example::Example(int val):itsVal(val){}
Example::operator unsigned int (){
return (itsVal);
}
int main(){
int theInt = 5;
Example exObject = theInt; // here
Example ctr(5);
int theInt1 = ctr; // here
return 0;
}
You can walk through that code with a debugger (and/or put a breakpoint on each of your constructors and operators) to see which of your constructors and operators is being invoked by which lines.
Because you didn't define them explicitly, the compiler also created a hidden/default copy constructor and assignment operator for your class. You can define these explicitly (as follows) if you want to use a debugger to see where/when they are being called.
Example::Example(const Example& rhs)
: itsVal(rhs.itsVal)
{}
Example& operator=(const Example& rhs)
{
if (this != &rhs)
{
this->itsVal = rhs.itsVal;
}
return *this;
}
int main() {
int theInt = 5;
/**
* Constructor "Example(int val)" in effect at the statement below.
* Same as "Example exObject(theInt);" or "Example exObject = Example(theInt);"
*/
Example exObject = theInt; // 1
Example ctr(5);
/**
* "operator unsigned int()" in effect at the statement below.
* What gets assigned is the value returned by "operator unsigned int()".
*/
int theInt1 = ctr; // 2
return 0;
}
At statement 1 the constructor Example(int val) is called. Declare it as explicit Example(int val) and you will get a compile time error i.e. no implicit conversion will then be allowed for this constructor.
All single argument constructors are called implicitly if the assigned value is of their respective argument type. Using the explicit keyword before single argument constructors disables implicit constructor calling and hence implicit conversion.
If the constructor was declared as explicit i.e. explicit Example(int val) then the following would happen for each statement.
Example exObject(theInt); // Compile time error.
Example exObject = theInt; // Compile time error.
Example exObject(Example(theInt)); // Okay!
Example exObject = Example(theInt); // Okay!
Also note that in case of implicit constructor call and hence implicit conversion the assigned value is an rvalue i.e. an un-named object implicitly created using an lvalue (theInt) which tells us that in case of implicit conversion the compiler converts
Example exObject = theInt;
to
Example exObject = Example(theInt);
So (in C++11) don't expect the lvalue constructor to be called seeing that you are using an lvalue i.e. a named value theInt for assignment. What gets called is the rvalue constructor since the assigned value is actually the un-named object created using the lvalue. However, this applies if you have both lvalue and rvalue versions of the constructor.
At statement 2 operator unsigned int() is called. Simply consider it as a normal function call with a weird name and the fact that it can get called automagically when an implicit conversion happens. The value returned by that function is the value assigned in the expression. And since in you implementation the value returned is an int it correctly gets assigned to int theInt1.
To be precise operator unsigned int() overloads () operator which is the cast operator. In your case it's overloaded for int hence whenever an object of Example class is assigned to an int the implicit type casting from Example to int takes place and hence operator unsigned int() gets called. Therefore,
int theInt1 = ctr;
is equivalent to
int theInt1 = (int)ctr;
Example exObject = theInt; // implicitly created copy constructor takes place
// object implicitly created from int and then copied
// it is like
Example exObject = Example(theInt);
// so it uses sequence
// Example(int) -> Example(const Example&)
int theInt1 = ctr; // operator int()
If you compiler supports copy constructor optimization and return value optimization you won't notice
Example(const Example&)
execution, but you can declare copy constructor to be private to understand what I am talking about.
Example exObject = theInt; // here
This uses implicit conversion of int to Example, effected by the non-explicit constructor which accepts an int.
This also requires the availability of copy constructor for Example, even though the compiler is allowed to omit copying the instance.
int theInt1 = ctr; // here
This uses implicit conversion of Example to unsigned int, provided by the cast operator.
Cast operators are normally avoided, since they tend to lead to confusing code, and you can mark single-argument constructors explicit, to disable implicit conversions to your class type. C++0x should add also the possibility to mark conversion operators explicit (so you'd need a static_cast to invoke them? - my compiler doesn't support them and all web resources seem to be concentrating on explicit conversion to bool).