Удивительная ценность про лямбду и 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