我目前正在尝试使用libc ++来编译和运行MSVC。在这样做的过程中,我遇到了一个令人讨厌的错误(至少我认为是一个错误),这已经花了我一段时间来确定。我有以下repro代码:
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返回后,全局变量 globalInt 的期望值是多少(未定义可能是可接受的答案)?
修改
我应该已经明确表示这在VS与MSVC 12中不起作用。在 foo 中,变量 rval 指向堆栈上的临时值,因此全局变量永远不会增加。
临时是在int&amp;&amp ;; operator *()const。如果我更换:
return static_cast<int&&>(*__i);
与
return std::move(*i);
然后一切都很顺利。使用C-cast也会导致临时创建。
答案 0 :(得分:8)
1)这是否合法,即我是否避免陷入其中 未定义的行为(它在语法上是合法的)?
你是如此,如此接近。 __i
是保留标识符,0xff
到char
的转换可能是实现定义的。除此之外,此代码有效且行为定义明确。
2)之后全局变量
globalInt
的期望值是多少 foo返回(未定义可能是一个可接受的答案)?
667
。 static_cast<int&&>(*__i)
返回的引用直接绑定到*__i
- 即globalInt
。不应该创建临时。 [expr.static.cast] / p3中的适用规则自C ++ 11以来基本保持不变,因此您肯定会在此处看到编译器错误。
根据对http://webcompiler.cloudapp.net/的测试,看起来这个bug已经在下一版VC ++中得到修复。
答案 1 :(得分:1)
在 C ++编程语言(第4版)中,Stroustrup声明(§7.7.2,第195页):
[...]标准库提供
move()
函数:move(x)
表示static_cast<X&&>(x)
其中X
是x
的类型。
更确切地说,从C ++ 11标准(iso.20.2.3):
template <class T> typename remove_reference<T>::type&& move(T&& t) noexcept;
返回:static_cast<typename remove_reference<T>::type&&>(t)
。
如果您的类型T
是int,则std::move()
和static_cast<int&&>()
完全相同。
因此,如果MSVC在从一个切换到另一个时给出不同的结果,那显然是一个错误。