C++临时应该是不变的吗?

Mar*_*ark 15 c++ language-lawyer visual-studio-2013 visual-studio-2015

我有一个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;
};
Run Code Online (Sandbox Code Playgroud)

我有以下代码:

const F a{3, 7};
const F b{5, 10};
auto result = F{a} *= b; // How does this compile?
Run Code Online (Sandbox Code Playgroud)

在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)'
Run Code Online (Sandbox Code Playgroud)

我的期望是F{a}创建一个非const临时副本,a然后operator *= (b)将临时对象分配给该临时副本result.我没想到临时是一个常数.有趣的是:auto result = F(a) *= b;在VS2015中编译没有错误,我认为应该在语义上相同.

我的问题是:VS2015或VS2013的行为是正确的?为什么?

非常感谢

Sha*_*our 8

Visual Studio 2015未生成正确的结果:

F{a}
Run Code Online (Sandbox Code Playgroud)

结果应该是一个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";
}
Run Code Online (Sandbox Code Playgroud)

Luc Danton提示VALUE_CATEGORY()代码.

使用webcompiler的 Visual Studio 具有相对较新的版本,可生成:

lvalue
Run Code Online (Sandbox Code Playgroud)

在这种情况下必须是const才能产生我们看到的错误.虽然gcc和clang(看到它直播)产生:

prvalue
Run Code Online (Sandbox Code Playgroud)

这可能与同样令人困惑的Visual Studio bug std :: move of string literal有关 - 哪个编译器是正确的?.

注意我们可以使用const F得到与gcc和clang相同的问题:

using cF = const F ;
auto result = cF{a} *= b; 
Run Code Online (Sandbox Code Playgroud)

因此,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.

请注意,据我所知,这不是"旧的MSVC左值投射错误".该问题的解决方案是使用/Zc:rvalueCast哪个不能解决此问题.这个问题在不正确添加cv-qualifier方面也有所不同,据我所知,这与前一个问题没有关系.


αλε*_*λυτ 0

我认为这是 VS2015 中的一个错误,因为如果您指定用户定义的复制构造函数:

F(const F&);
Run Code Online (Sandbox Code Playgroud)

或 make 变量a非常量代码将成功编译。

看起来对象的常量性已a转移到新创建的对象中。