我有一个C ++类,它具有以下接口:
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;
};
我有以下代码:
const F a{3, 7};
const F b{5, 10};
auto result = F{a} *= b; // How does this compile?
在Visual Studio(VS)2013下,注释行编译时没有错误。在VS2015下,产生错误C2678:
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)'
我的期望是F{a}
会创建a
的非常量临时副本,operator *= (b)
将被应用到result
之后,临时对象将被分配给auto result = F(a) *= b;
}。我没想到临时是一个常数。有趣的是:/******************************************
Subway navigation (by sOltan stackoverflow.com)
*******************************************/
.subway-station {
color: rgb(255, 255, 255);
height: 100%;
width: 100px;
position: relative;
}
.subway-open {
top: 60px; /* start with a margin from the top of the table cell */
height: 100%; /* draw through the circled number */
left: 40px; /* indent this much */
width: 10px;
position: absolute; /* position relative to ancestor subway-station */
z-index: 10;
color: rgb(68, 68, 68);
background: rgb(205, 205, 205) none repeat scroll 0% 0% / auto padding-box border-box;
}
.subway-close {
top: 0px; /* start from the top of the table cell */
height: 50px; /* draw line this much from top */
left: 40px; /* indent this much */
width: 10px; /* width of the line */
position: absolute; /* position relative to ancestor subway-station */
z-index: 10;
color: rgb(68, 68, 68);
background: rgb(205, 205, 205) none repeat scroll 0% 0% / auto padding-box border-box;
}
.subway-line {
bottom: 0px; /* this indicates to start drawing line from the bottom */
height: 100%; /* draw all the way to the top of the table cell */
left: 40px; /* indent this much */
width: 10px;
position:absolute; /* position relative to ancestor subway-station */
z-index: 10;
color: rgb(68, 68, 68);
background: rgb(205, 205, 205) none repeat scroll 0% 0% / auto padding-box border-box;
}
.number {
font-family: 'Lato';
z-index: 20;
position: absolute;
left: 0px;
}
.circled {
display:inline-block;
margin: 10px;
border-radius: 50%;
padding: 10px 20px;
background: #fff;
border: 8px solid #CDCDCD;
color: #CDCDCD;
text-align: center;
font: 32px Arial, sans-serif;
z-index: 20;
position: absolute;
}
.circled:hover {
background-color: #586CEF !important;
color: white;
}
在VS2015中编译没有错误,我认为它应该在语义上相同。
我的问题是:哪种行为是正确的VS2015或VS2013&为什么?
非常感谢
答案 0 :(得分:8)
Visual Studio 2015没有为以下内容生成正确的结果:
F{a}
结果应该是一个prvalue( gcc和clang都有这个结果),但它产生一个左值。我使用OP的代码的以下修改版本来产生这个结果:
#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";
}
帽子提示Luc Danton for the VALUE_CATEGORY() code。
使用具有相对较新版本的webcompiler的Visual Studio产生:
lvalue
在这种情况下必须是 const 才能产生我们看到的错误。虽然gcc和clang( see it live )都会产生:
prvalue
这可能与同样令人困惑的Visual Studio错误std::move of string literal - which compiler is correct?有关。
注意我们可以使用 const F 来解决gcc和clang的相同问题:
using cF = const F ;
auto result = cF{a} *= b;
因此,Visual Studio不仅为我们提供了错误的值类别,而且还随意添加了一个cv-qualifier。
汉斯在他对你的问题的评论中指出,使用F(a)
会产生预期的结果,因为它正确地产生了一个prvalue。
C ++标准草案的相关部分是5.2.3
[expr.type.conv] 部分,其中说:
类似地,一个simple-type-specifier或typename-specifier后跟一个braced-init-list创建一个临时的 具有指定的braced-init-list的指定类型direct-list-initialized(8.5.4)的对象,其值为 该临时对象作为prvalue。
请注意,据我所知,这不是"old MSVC lvalue cast bug"。该问题的解决方案是使用/Zc:rvalueCast
来解决此问题。这个问题在不正确添加cv-qualifier方面也有所不同,据我所知,这与上一期不会发生。
答案 1 :(得分:0)
我的想法是VS2015中的一个错误,因为如果您指定用户定义的复制构造函数:
F(const F&);
或使变量a
非常规代码将成功编译。
将a
中的对象常量转换为新创建的对象。
答案 2 :(得分:0)
Visual C ++在一段时间内遇到了一个错误,即身份转换不会产生临时变量,但会引用原始变量。
此处有错误报告:identity cast to non-reference type violates standard
答案 3 :(得分:0)
来自http://en.cppreference.com/w/cpp/language/copy_elision:
在下列情况下,允许编译器省略 即使复制/移动构造函数,也可以复制和移动类对象的构造函数 析构函数有可观察到的副作用。
.......
当无名临时未绑定到任何引用时,将被移动或 复制到相同类型的对象中(忽略顶级cv - 资格),省略了复制/移动。那个临时的时候 在构造中,它直接在存储器中构造 否则被移动或复制到。当无名的临时是 返回语句的参数,这种复制省略的变体被称为 RVO,“返回值优化”。
因此编译器可以选择忽略副本(在这种情况下,它将作为非const类型的隐式转换)。