Удивительная ценность про лямбду и cin.get

Среда: vs2013 rc5 / vs2017;
проект: консольное приложение win32 ;

представление: скомпилируйте и запустите немного, затем прервите и просмотрите переменную "task_";
если "add_task(&Test::print, &t, str, 10)" в func main, "task_" - правильное значение;

но если "add_task(&Test::print, &t, str, 10)" в func mytest, "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;
}

2 ответа

В C++ компилятору не требуется делать именно то, что вы думаете, если бы он выполнял ваш код построчно. Он может оптимизироваться в соответствии с правилом "как будто", что означает, что он может делать что-то еще, что будет происходить быстрее, если наблюдаемое поведение будет таким же.

Наблюдаемое поведение не включает запуск кода в отладчике.

Переменная task_ никогда не имеет никаких последствий для любого наблюдаемого поведения, поэтому соответствующий компилятор может оптимизировать его полностью. Функция add_task не влияет на наблюдаемое поведение, поэтому соответствующий компилятор может полностью его оптимизировать.

(Я не уверен, будет ли это на самом деле здесь, но только потому, что std::function может потребоваться сделать динамическое распределение, и когда это произойдет, компилятору становится сложнее рассуждать о наблюдаемых побочных эффектах.)

Кроме того, так как my_test не имеет никаких последствий для чего-либо затронутого std::cin.get()компилятор может свободно переупорядочивать эти два оператора, если считает, что он может работать быстрее.

Вы должны сделать что-то вроде, запустить функцию в test_ переменная, если вы хотите заставить оптимизатор выполнять действия в определенном порядке или фиксировать определенное содержимое памяти в определенное время.

Спасибо твой ответ. В действительности, я запускаю отладочную версию и закрываю все оптимизации, это происходит как раньше. в неправильном случае я вставляю task_() только перед std::cin.get(), это приведет к сбою:

    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  
Другие вопросы по тегам