C++ 全局与局部函数对象

C++ 全局与局部函数对象,c++,c++11,C++,C++11,我有一个函数返回另一个函数,它捕获包装器函数的参数。本地版本有效,但全局版本无效,我不明白为什么: #include <iostream> #include <sstream> #include <iomanip> const auto& parseDateTimeWithFormat = [](const std::string& formatStr) { return [&formatStr](const std::st

我有一个函数返回另一个函数,它捕获包装器函数的参数。本地版本有效,但全局版本无效,我不明白为什么:

#include <iostream>
#include <sstream>
#include <iomanip>


const auto& parseDateTimeWithFormat = [](const std::string& formatStr) {
    return [&formatStr](const std::string& dateTimeStr) {
        std::cout << formatStr << std::endl;
        tm t = {};
        std::istringstream ss(dateTimeStr);
        ss >> std::get_time(&t, formatStr.c_str());
        const int timestamp = (int)mktime(&t);
        return timestamp;
    };
};


const auto& parseDateTime1 = parseDateTimeWithFormat("%Y-%m-%dT%H:%M:%SZ");


int main(int argc, const char* argv[]) {
    int ts1 = parseDateTime1("2018-10-08T10:09:08Z");
    std::cout << ts1 << std::endl;

    const auto& parseDateTime2 = parseDateTimeWithFormat("%Y-%m-%dT%H:%M:%SZ");
    int ts2 = parseDateTime2("2018-10-08T10:09:08Z");
    std::cout << ts2 << std::endl;

    return 0;
}

此外,当通过值而不是引用捕获
formatStr
时,全局版本也可以工作。

本地版本可能“工作”,但它也会遇到与全局版本相同的问题,即未定义的行为。在所有情况下,当调用
parseDateTimeWithFormat
时,都会给出一个字符串文本。因为这不是一个
std::string
,所以会为您创建一个临时字符串。这个字符串就是您在中捕获的内容

return [&formatStr](const std::string& dateTimeStr) {
    std::cout << formatStr << std::endl;
    tm t = {};
    std::istringstream ss(dateTimeStr);
    ss >> std::get_time(&t, formatStr.c_str());
    const int timestamp = (int)mktime(&t);
    return timestamp;
};
return[&formatStr](常量std::string&dateTimeStr){
std::cout std::get_time(&t,formatStr.c_str());
常量int timestamp=(int)mktime(&t);
返回时间戳;
};

然后从lambda回来。不幸的是,一旦在调用
parseDateTimeWithFormat
的表达式结束时,临时文件就会被销毁,剩下的是对
formatStr
的悬空引用。正如您所发现的,修复方法是按值捕获,这样lambda就有了自己的副本,您就不会试图引用不再存在的内容。

您的本地版本可能会“工作”,但它也会遇到与全局版本相同的问题,这是一种未定义的行为。在所有情况下,当调用
parseDateTimeWithFormat
时,都会给出一个字符串文本。因为这不是一个
std::string
,所以会为您创建一个临时字符串。这个字符串就是您在中捕获的内容

return [&formatStr](const std::string& dateTimeStr) {
    std::cout << formatStr << std::endl;
    tm t = {};
    std::istringstream ss(dateTimeStr);
    ss >> std::get_time(&t, formatStr.c_str());
    const int timestamp = (int)mktime(&t);
    return timestamp;
};
return[&formatStr](常量std::string&dateTimeStr){
std::cout std::get_time(&t,formatStr.c_str());
常量int timestamp=(int)mktime(&t);
返回时间戳;
};

然后从lambda回来。不幸的是,一旦在调用
parseDateTimeWithFormat
的表达式结束时,临时文件就会被销毁,剩下的是对
formatStr
的悬空引用。正如您所发现的,修复方法是按值捕获,这样lambda就有了自己的副本,您就不会试图引用不再存在的内容。

因为您正在捕获对本地参数的引用,它本身是对临时的引用,并在函数返回后被销毁。
parseDateTimeWithFormat
可以只是一个常规函数。请注意,C++中的lambda不是某种“高阶函数”。看起来像XY问题。@ VTT,没错,实际上它不是一个高阶函数。因为您正在捕获对局部参数的引用,它本身是对临时的引用,并在函数返回后被销毁。
parseDateTimeWithFormat
可以只是一个常规函数。请注意,C++中的lambda不是某种“高阶函数”。看起来像XY问题。“VTT,你说的对,实际上它不是一个高阶函数。弥敦说了什么!”内森说了什么!:)