0

环境:vs2013 rc5 / vs2017;<br>项目:win32控制台应用;</p>

表示:编译运行一会儿,然后中断观察变量“task_”;
如果 func main 中的“add_task(&Test::print, &t, str, 10)”,则“task_”是正确的值;

但是如果 func mytest 中的“add_task(&Test::print, &t, str, 10)”,则“task_”的值是错误的;如果将 std::cin.get() 替换为 while(1){},则右转;


#include <iostream>
#include <string>
#include <chrono>
#include <thread>
#include <functional>

using task_t = std::function<void()>;

class Test
{
public:
    void print(const std::string& str, int i)
    {
    std::cout << "Test: " << str << ", i: " << i << std::endl;
    }
};

template<typename Function, typename Self, typename... Args>
void add_task(const Function& func, Self* self, Args... args)
{
    task_t task = [&func, &self, args...]{ return (*self.*func)(args...); };    
    task_ = task;
}

Test t;
std::string str = "Hello world";

task_t task_ = nullptr;

void mytest()
{
    add_task(&Test::print, &t, str, 10);
}

int main()
{
    mytest();
    std::cin.get();
    return 0;
}
4

2 回答 2

0

在 C++ 中,如果编译器逐行执行您的代码,则不需要完全按照您的想法去做。它可以根据“as-if”规则进行优化,这意味着如果可观察的行为相同,它可以做一些更快的事情。

可观察行为不包括在调试器中运行代码。

该变量task_永远不会对任何可观察的行为产生任何影响,因此符合标准的编译器可以完全优化它。该函数add_task不会影响任何可观察到的行为,因此符合标准的编译器可以完全优化它。

(我不确定它是否真的会在这里这样做,但这只是因为std::function可能必须进行动态分配,而当这种情况发生时,编译器就很难推断出可观察到的副作用。)

此外,由于my_test对 所涉及的任何内容都没有任何后果std::cin.get(),因此如果编译器认为它可能运行得更快,它可以自由地重新排序这两个语句。

test_如果您想强制优化器以特定顺序执行操作,或者在特定时间提交特定内存内容,您应该执行以下操作:在变量中运行函数。

于 2017-09-25T06:54:17.960 回答
0

谢谢你的回答。实际上,我运行调试版本并关闭所有优化,它像以前一样发生。在错误的情况下,我在 std::cin.get() 之前插入 task_(),它会崩溃:

    cccccccc()  Unknown
    [Frames below may be incorrect and/or missing]  
>   tp_test.exe!add_task::__l3::<lambda>() Line 21  C++
    tp_test.exe!std::_Callable_obj<void <lambda>(void),0>::_ApplyX<void>() Line 284 C++
    tp_test.exe!std::_Func_impl<std::_Callable_obj<void <lambda>(void),0>,std::allocator<std::_Func_class<void> >,void>::_Do_call() Line 229    C++
    tp_test.exe!std::_Func_class<void>::operator()() Line 315   C++
    tp_test.exe!main() Line 39  C++
    tp_test.exe!__tmainCRTStartup() Line 626    C
    tp_test.exe!mainCRTStartup() Line 466   C
    kernel32.dll!765c8744() Unknown
    ntdll.dll!__RtlUserThreadStart()    Unknown
    ntdll.dll!__RtlUserThreadStart@8()  Unknown

所以我调试,在task_()处断点,按F11并反汇编,在文件“功能”处崩溃:

    _Ret operator()(_Types... _Args) const
        {   // call through stored object
00BE36A0  push        ebp  
00BE36A1  mov         ebp,esp  
00BE36A3  sub         esp,0CCh  
00BE36A9  push        ebx  
00BE36AA  push        esi  
00BE36AB  push        edi  
00BE36AC  push        ecx  
00BE36AD  lea         edi,[ebp-0CCh]  
00BE36B3  mov         ecx,33h  
00BE36B8  mov         eax,0CCCCCCCCh  
00BE36BD  rep stos    dword ptr es:[edi]  
00BE36BF  pop         ecx     <==========crash
00BE36C0  mov         dword ptr [this],ecx  
于 2017-09-25T07:32:03.563 回答