C++右值引用行为(具体示例)

C++ rvalue reference behaviour (specific example)

我目前正在尝试使用 MSVC 编译 libc++ 和 运行。在这样做的过程中,我遇到了一个讨厌的错误(至少我认为是一个错误),我花了一段时间才确定下来。我有以下重现代码:

int globalInt = 666;

class mini_move_iterator
{
public:

    mini_move_iterator(int* i) : __i(i){}

    int&& operator*() const
    {
        return static_cast<int&&>(*__i);
    }

    int* __i;
};

void foo(int&& rval)
{
    // Smash stack
    char stackUser[1000];
    for (int i = 0; i < 1000; ++i)
        stackUser[i] = 0xff;

    rval += 1;
}

int main()
{
    mini_move_iterator mmi(&globalInt);
    foo(*mmi);
    return 0;
}

我有几个问题:

1) 这是否合法,即我是否避免误入未定义行为的领域(这在语法上肯定是合法的)?

2) foo returns 之后的全局变量 globalInt 的期望值是多少(undefined 可能是一个可以接受的答案)?

编辑:

我应该明确指出,这在 VS 和 MSVC 12 中不起作用。在 foo 中,变量 rval 指向堆栈上的临时变量,因此永远不会增加全局变量。

临时文件是在 int&& operator*() const 中创建的。如果我替换:

return static_cast<int&&>(*__i);

return std::move(*i);

那么就万事大吉了。使用 C-cast 也会导致创建临时文件。

1) Is this legal, i.e. have I avoided straying into the realms of undefined behaviour (it is certainly syntactically legal)?

你们是如此,如此亲密。 __i 是保留标识符,0xffchar 的转换可能是实现定义的。除此之外,此代码有效且行为定义明确。

2) What is the expected value of the global variable globalInt after foo returns (undefined may be an acceptable answer)?

667static_cast<int&&>(*__i) 返回的引用直接绑定到 *__i - 即 globalInt。不应创建临时文件。 [expr.static.cast]/p3 中的适用规则自 C++11 以来基本保持不变,因此您肯定会在此处看到编译器错误。

根据对 http://webcompiler.cloudapp.net/ 的测试,看起来这个错误已在 VC++ 的下一版本中得到修复。

C++ 编程语言(第 4 版) 中,Stroustrup 指出(§7.7.2,第 195 页):

[...] the standard library provides a move() function: move(x) means static_cast<X&&>(x) where X is the type of x.

更准确地说,来自 C++11 标准 (iso.20.2.3):

template <class T> typename remove_reference<T>::type&& move(T&& t) noexcept; Returns: static_cast<typename remove_reference<T>::type&&>(t).

如果你的类型 T 是一个 int,std::move()static_cast<int&&>() 是完全一样的。

因此,如果 MSVC 在从一个切换到另一个时给出不同的结果,这显然是一个错误。