I have a C++ class that has the following interface:
class F {
public:
F(int n, int d);
// no other constructors/assignment constructors defined
F& operator *= (const F&);
F& operator *= (int);
int n() const;
int d() const;
};
And I have the following code:
const F a{3, 7};
const F b{5, 10};
auto result = F{a} *= b; // How does this compile?
Under Visual Studio (VS) 2013, the commented line compiles without error. Under VS2015 , error C2678 is produced:
error C2678: binary '*=': no operator found
which takes a left-hand operand of type 'const F'
(or there is no acceptable conversion)
note: could be 'F &F::operator *=(const F &)'
note: or 'F &F::operator *=(int)'
note: while trying to match the argument list '(const F, const F)'
My expectation was that F{a}
would create a non-const temporary copy of a
to which operator *= (b)
would be applied, after which the temporary object would be assigned to result
. I did not expect the temporary to be a constant. Interestingly: auto result = F(a) *= b;
compiles without error in VS2015, which I thought should be semantically the same.
My question is: which behaviour is correct VS2015 or VS2013 & why?
Many thanks
The startup process for a furnace consumes more energy than simply running the entire time at one set temperature, costing you additional money. To maximize efficiency, it's best to keep your furnace (or AC) at the same consistent temperature for long periods of time.
In general, it is cheaper to leave the AC on all day during very hot temperatures. However, it's not efficient to keep it on full blast all the time. Your exact results may vary depending on your AC system. So, it's best to consult a professional if you aren't sure about how to lower your energy bills.
Because constantly changing the temperature setting on your thermostat causes your air conditioner to stop and start often, which causes them to run inefficiently. In order to save money, your AC should be running for longer, steadier.
Short AC CycleIt is an indication that something is wrong with your AC because the compressor keeps turning on and off in under 10 minutes. Since we know a cycle should last between 15 and 20 minutes, it's safe to say that 10 minutes is on the lower side.
Visual Studio 2015 is not producing the correct result for:
F{a}
The result should be a prvalue(gcc and clang both have this result) but it is producing an lvalue. I am using the following modified version of the OP's code to produce this result:
#include <iostream>
class F {
public:
F(int n, int d) :n_(n), d_(d) {};
F(const F&) = default ;
F& operator *= (const F&){return *this; }
F& operator *= (int) { return *this; }
int n() const { return n_ ; }
int d() const { return d_ ; }
int n_, d_ ;
};
template<typename T>
struct value_category {
static constexpr auto value = "prvalue";
};
template<typename T>
struct value_category<T&> {
static constexpr auto value = "lvalue";
};
template<typename T>
struct value_category<T&&> {
static constexpr auto value = "xvalue";
};
#define VALUE_CATEGORY(expr) value_category<decltype((expr))>::value
int main()
{
const F a{3, 7};
const F b{5, 10};
std::cout << "\n" << VALUE_CATEGORY( F{a} ) << "\n";
}
Hat tip to Luc Danton for the VALUE_CATEGORY() code.
Visual Studio using webcompiler which has a relatively recent version produces:
lvalue
which must be const in this case to produce the error we are seeing. While both gcc and clang (see it live) produce:
prvalue
This may be related to equally puzzling Visual Studio bug std::move of string literal - which compiler is correct?.
Note we can get the same issue with gcc and clang using a const F:
using cF = const F ;
auto result = cF{a} *= b;
so not only is Visual Studio giving us the wrong value category but it also arbitrarily adding a cv-qualifier.
As Hans noted in his comments to your question using F(a)
produces the expected results since it correctly produces a prvalue.
The relevant section of the draft C++ standard is section 5.2.3
[expr.type.conv] which says:
Similarly, a simple-type-specifier or typename-specifier followed by a braced-init-list creates a temporary object of the specified type direct-list-initialized (8.5.4) with the specified braced-init-list, and its value is that temporary object as a prvalue.
Note, as far as I can tell this is not the "old MSVC lvalue cast bug". The solution to that issue is to use /Zc:rvalueCast
which does not fix this issue. This issue also differs in the incorrect addition of a cv-qualifier which as far as I know does not happen with the previous issue.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With