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
是保留标识符,0xff
到 char
的转换可能是实现定义的。除此之外,此代码有效且行为定义明确。
2) What is the expected value of the global variable globalInt
after
foo returns (undefined may be an acceptable answer)?
667
。 static_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 在从一个切换到另一个时给出不同的结果,这显然是一个错误。
我目前正在尝试使用 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
是保留标识符,0xff
到 char
的转换可能是实现定义的。除此之外,此代码有效且行为定义明确。
2) What is the expected value of the global variable
globalInt
after foo returns (undefined may be an acceptable answer)?
667
。 static_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)
meansstatic_cast<X&&>(x)
whereX
is the type ofx
.
更准确地说,来自 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 在从一个切换到另一个时给出不同的结果,这显然是一个错误。