如何编写一个包含可选 Return 值的函数的 Lambda
How to Write a Lambda Wrapping a Function with Optional Return Value
我尝试编写一个 lambda 来测量任意函数的执行时间。在很多帮助下,我已经针对 C++14 和具有 return 值的函数进行了管理,请参阅 .
然后我希望我的代码也适用于 C++11,因此我用模板函数实现了相同的想法。
最后我意识到这段代码不适用于没有 return 值的函数。概括模板函数以对函数 returning void.
启用时间测量非常简单
但是当涉及到测量 lambda 时,我被卡住了。编译器抱怨我要使用的可选 return 值的类型不完整。有可能解决这个问题吗?
这是我的代码:
#include <chrono>
#include <iostream>
#include <set>
#include <boost/config.hpp>
#ifdef BOOST_NO_CXX14_GENERIC_LAMBDAS
/**
* \brief Measures the time of arbitrary function calls.
*
* This template function works with C++11 and therefore it does not use
* universal references.
*
* This is an internal helper template for functions returning void.
*
* \tparam Function function type
* \tparam Parameters parameters type
* \param enabled whether time measurement should be enabled
* \param taskName name for printing the measured time
* \param function function to measure
* \param parameters function arguments
*
* \returns return value from given function
*/
template <typename Function, typename... Parameters>
auto measure(std::true_type, bool enabled,
const std::string& taskName, Function function, Parameters... parameters) ->
decltype(function(parameters...))
{
std::chrono::steady_clock::time_point startTimePoint;
if (enabled)
{
startTimePoint = std::chrono::steady_clock::now();
}
std::forward<decltype(function)>(function)(
std::forward<decltype(parameters)>(parameters)...);
if (enabled)
{
const std::chrono::steady_clock::time_point stopTimePoint =
std::chrono::steady_clock::now();
const std::chrono::duration<double> timeSpan =
std::chrono::duration_cast<std::chrono::duration<double>>(
stopTimePoint - startTimePoint);
std::cout << taskName << " took " << timeSpan.count() << " seconds." <<
std::endl;
}
}
/**
* \brief Measures the time of arbitrary function calls.
*
* This template function works with C++11 and therefore it does not use
* universal references.
*
* This is an internal helper template for functions returning non-void.
*
* \tparam Function function type
* \tparam Parameters parameters type
* \param enabled whether time measurement should be enabled
* \param taskName name for printing the measured time
* \param function function to measure
* \param parameters function arguments
*
* \returns return value from given function
*/
template <typename Function, typename... Parameters>
auto measure(std::false_type, bool enabled,
const std::string& taskName, Function function, Parameters... parameters) ->
decltype(function(parameters...))
{
std::chrono::steady_clock::time_point startTimePoint;
if (enabled)
{
startTimePoint = std::chrono::steady_clock::now();
}
auto returnValue =
std::forward<decltype(function)>(function)(
std::forward<decltype(parameters)>(parameters)...);
if (enabled)
{
const std::chrono::steady_clock::time_point stopTimePoint =
std::chrono::steady_clock::now();
const std::chrono::duration<double> timeSpan =
std::chrono::duration_cast<std::chrono::duration<double>>(
stopTimePoint - startTimePoint);
std::cout << taskName << " took " << timeSpan.count() << " seconds." <<
std::endl;
}
return returnValue;
}
template <typename Function, typename... Parameters>
using ReturnType = typename std::result_of<Function(Parameters...)>::type;
/**
* \brief Measures the time of arbitrary function calls.
*
* This template function works with C++11 and therefore it does not use
* universal references.
*
* \tparam Function function type
* \tparam Parameters parameters type
* \param enabled whether time measurement should be enabled
* \param taskName name for printing the measured time
* \param function function to measure
* \param parameters function arguments
*
* \returns return value from given function
*/
template <typename Function, typename... Parameters>
auto measure(bool enabled, const std::string& taskName, Function function,
Parameters... parameters) -> decltype(function(parameters...))
{
return measure(std::is_void<ReturnType<Function, Parameters...>>{},
enabled, taskName, function, parameters...);
}
#else
/**
* \brief Measures the time of arbitrary function calls.
*
* This lambda works with C++14 and it accepts universal references.
*
* \param enabled whether time measurement should be enabled
* \param taskName name for printing the measured time
* \param function function to measure
* \param parameters function arguments
*
* \returns return value from given function
*/
auto measure = [](bool enabled, const std::string& taskName, auto&& function,
auto&&... parameters) -> decltype(auto)
{
std::chrono::steady_clock::time_point startTimePoint;
if (enabled)
{
startTimePoint = std::chrono::steady_clock::now();
}
decltype(auto) returnValue =
std::forward<decltype(function)>(function)(
std::forward<decltype(parameters)>(parameters)...);
if (enabled)
{
const std::chrono::steady_clock::time_point stopTimePoint =
std::chrono::steady_clock::now();
const std::chrono::duration<double> timeSpan =
std::chrono::duration_cast<std::chrono::duration<double>>(
stopTimePoint - startTimePoint);
std::cout << taskName << " took " << timeSpan.count() << " seconds." <<
std::endl;
}
return returnValue;
};
#endif
int main(int, char**)
{
measure(true, "Populating Ordered Set", []()
{
std::set<int> orderedSet;
for (int i = 0; i < 1000; ++i)
{
orderedSet.insert(i);
}
});
return 0;
}
如果它是用 C++11 编译器编译的(比如 g++ 和 -std=gnu++11),它使用模板函数,因此在这里工作得很好。如果它是使用 C++14 编译器 (-std=gnu++14) 编译的,它会使用 lambda,因此我会收到此编译错误消息:
..\src\Main.cpp: In instantiation of '<lambda(bool, const string&, auto:1&&, auto:2&& ...)> [with auto:1 = main(int, char**)::<lambda()>; auto:2 = {}; std::__cxx11::string = std::__cxx11::basic_string<char>]':
..\src\Main.cpp:186:10: required from here
..\src\Main.cpp:154:24: error: 'void returnValue' has incomplete type
decltype(auto) returnValue =
^~~~~~~~~~~
非常感谢您的帮助。
if it is compiled with a C++14 compiler (-std=gnu++14), it uses the lambda and therefore I get this compilation error message
让我用下面的伪代码简化你的函数
auto measure = [](someArguments....) -> decltype(auto)
{
something1();
decltype(auto) returnValue = somethingThatCanReturnVoid();
something2();
return returnValue;
};
问题出在 somethingThatCanReturnVoid()
return void
因为你不能定义 void
变量。
您可以使用以下事实
(1) 你不能定义一个 void
变量但是你可以写 return foo();
其中 foo()
是一个函数 returning void
(2) 如果你写return foo()
,函数范围内对象的销毁在foo()
执行后执行
在这一点上,解决方案似乎很明显:创建一个 Bar()
类型的对象并在 Bar
析构函数中执行 something2()
。
如下(伪代码)
auto measure = [](someArguments....) -> decltype(auto)
{
Bar b{otherArguments...}; // something1() in contruction;
// something2() in destruction;
return somethingThatCanReturnVoid();
};
这样,something1()
在somethingThatCanReturnVoid()
之前执行,something2()
在somethingThatCanReturnVoid()
之后执行,编译器不会抱怨
return somethingThatCanReturnVoid();
当 somethingThatCanReturnVoid()
return void
时也是完全合法的
使用 RAII 代替您的计时:
struct Timer
{
explicit Timer(bool enable) : enable(enable)
{
if (enabled)
{
startTimePoint = std::chrono::steady_clock::now();
}
}
~Timer()
{
if (enabled)
{
const std::chrono::steady_clock::time_point stopTimePoint =
std::chrono::steady_clock::now();
const std::chrono::duration<double> timeSpan =
std::chrono::duration_cast<std::chrono::duration<double>>(
stopTimePoint - startTimePoint);
std::cout << taskName << " took " << timeSpan.count() << " seconds." <<
std::endl;
}
}
Timer(const Timer&) = delete;
Timer& operator=(const Timer&) = delete;
bool enable;
std::chrono::steady_clock::time_point startTimePoint;
};
然后你的函数变成:
template <typename Function, typename... Args>
auto measure(bool enabled, const std::string& taskName, Function&& function, Args&&... args)
-> decltype(std::forward<Function>(function)(std::forward<Args>(args)...))
{
Timer timer(enabled);
return std::forward<Function>(function)(std::forward<Args>(args)...);
}
有了使用 RAII 的想法,也可以简化模板代码。对于那些可能觉得方便的人,我想透露一下我的最终版本:
#include <chrono>
#include <iostream>
#include <set>
#include <boost/config.hpp>
/**
* \brief Internal timer that can be used to measure time with RAII.
*/
class InternalTimer
{
public:
/**
* \brief Instance creation starts the timer.
*
* \param enabled whether time measurement should be enabled
* \param taskName name for printing the measured time
*/
explicit InternalTimer(bool enabled, const std::string& taskName) :
enabled(enabled), taskName(taskName)
{
if (enabled)
{
startTimePoint = std::chrono::steady_clock::now();
}
}
/**
* \brief Destructing the instance stops the timer and prints the measurement.
*/
~InternalTimer()
{
if (enabled)
{
const std::chrono::steady_clock::time_point stopTimePoint =
std::chrono::steady_clock::now();
const std::chrono::duration<double> timeSpan = std::chrono::duration_cast<
std::chrono::duration<double>>(stopTimePoint - startTimePoint);
std::cout << taskName << " took " << timeSpan.count() << " seconds."
<< std::endl;
}
}
/**
* \brief Deleted copy constructor.
*/
InternalTimer(const InternalTimer&) = delete;
/**
* \brief Deleted assignment operator.
*
* \returns reference to the object that is assigned to
*/
InternalTimer& operator=(const InternalTimer&) = delete;
private:
bool enabled;
const std::string& taskName;
std::chrono::steady_clock::time_point startTimePoint;
};
#ifdef BOOST_NO_CXX14_GENERIC_LAMBDAS
/**
* \brief Measures the time of arbitrary function calls.
*
* This template function works with C++11 and therefore it does not use
* universal references.
*
* \tparam Function function type
* \tparam Parameters parameters type
* \param enabled whether time measurement should be enabled
* \param taskName name for printing the measured time
* \param function function to measure
* \param parameters function arguments
*
* \returns return value from given function
*/
template <typename Function, typename... Parameters>
auto measure(bool enabled, const std::string& taskName, Function function,
Parameters... parameters) -> decltype(function(parameters...))
{
InternalTimer timer(enabled, taskName);
return std::forward<Function>(function)(
std::forward<Parameters>(parameters)...);
}
#else
/**
* \brief Measures the time of arbitrary function calls.
*
* This lambda works with C++14 and it accepts universal references.
*
* \param enabled whether time measurement should be enabled
* \param taskName name for printing the measured time
* \param function function to measure
* \param parameters function arguments
*
* \returns return value from given function
*/
auto measure = [](bool enabled, const std::string& taskName, auto&& function,
auto&&... parameters) -> decltype(auto)
{
InternalTimer timer(enabled, taskName);
return std::forward<decltype(function)>(function)(
std::forward<decltype(parameters)>(parameters)...);
};
#endif
int main(int, char**)
{
measure(true, "Populating Ordered Set", []()
{
std::set<int> orderedSet;
for (int i = 0; i < 1000; ++i)
{
orderedSet.insert(i);
}
});
return 0;
}
我尝试编写一个 lambda 来测量任意函数的执行时间。在很多帮助下,我已经针对 C++14 和具有 return 值的函数进行了管理,请参阅
然后我希望我的代码也适用于 C++11,因此我用模板函数实现了相同的想法。
最后我意识到这段代码不适用于没有 return 值的函数。概括模板函数以对函数 returning void.
启用时间测量非常简单但是当涉及到测量 lambda 时,我被卡住了。编译器抱怨我要使用的可选 return 值的类型不完整。有可能解决这个问题吗?
这是我的代码:
#include <chrono>
#include <iostream>
#include <set>
#include <boost/config.hpp>
#ifdef BOOST_NO_CXX14_GENERIC_LAMBDAS
/**
* \brief Measures the time of arbitrary function calls.
*
* This template function works with C++11 and therefore it does not use
* universal references.
*
* This is an internal helper template for functions returning void.
*
* \tparam Function function type
* \tparam Parameters parameters type
* \param enabled whether time measurement should be enabled
* \param taskName name for printing the measured time
* \param function function to measure
* \param parameters function arguments
*
* \returns return value from given function
*/
template <typename Function, typename... Parameters>
auto measure(std::true_type, bool enabled,
const std::string& taskName, Function function, Parameters... parameters) ->
decltype(function(parameters...))
{
std::chrono::steady_clock::time_point startTimePoint;
if (enabled)
{
startTimePoint = std::chrono::steady_clock::now();
}
std::forward<decltype(function)>(function)(
std::forward<decltype(parameters)>(parameters)...);
if (enabled)
{
const std::chrono::steady_clock::time_point stopTimePoint =
std::chrono::steady_clock::now();
const std::chrono::duration<double> timeSpan =
std::chrono::duration_cast<std::chrono::duration<double>>(
stopTimePoint - startTimePoint);
std::cout << taskName << " took " << timeSpan.count() << " seconds." <<
std::endl;
}
}
/**
* \brief Measures the time of arbitrary function calls.
*
* This template function works with C++11 and therefore it does not use
* universal references.
*
* This is an internal helper template for functions returning non-void.
*
* \tparam Function function type
* \tparam Parameters parameters type
* \param enabled whether time measurement should be enabled
* \param taskName name for printing the measured time
* \param function function to measure
* \param parameters function arguments
*
* \returns return value from given function
*/
template <typename Function, typename... Parameters>
auto measure(std::false_type, bool enabled,
const std::string& taskName, Function function, Parameters... parameters) ->
decltype(function(parameters...))
{
std::chrono::steady_clock::time_point startTimePoint;
if (enabled)
{
startTimePoint = std::chrono::steady_clock::now();
}
auto returnValue =
std::forward<decltype(function)>(function)(
std::forward<decltype(parameters)>(parameters)...);
if (enabled)
{
const std::chrono::steady_clock::time_point stopTimePoint =
std::chrono::steady_clock::now();
const std::chrono::duration<double> timeSpan =
std::chrono::duration_cast<std::chrono::duration<double>>(
stopTimePoint - startTimePoint);
std::cout << taskName << " took " << timeSpan.count() << " seconds." <<
std::endl;
}
return returnValue;
}
template <typename Function, typename... Parameters>
using ReturnType = typename std::result_of<Function(Parameters...)>::type;
/**
* \brief Measures the time of arbitrary function calls.
*
* This template function works with C++11 and therefore it does not use
* universal references.
*
* \tparam Function function type
* \tparam Parameters parameters type
* \param enabled whether time measurement should be enabled
* \param taskName name for printing the measured time
* \param function function to measure
* \param parameters function arguments
*
* \returns return value from given function
*/
template <typename Function, typename... Parameters>
auto measure(bool enabled, const std::string& taskName, Function function,
Parameters... parameters) -> decltype(function(parameters...))
{
return measure(std::is_void<ReturnType<Function, Parameters...>>{},
enabled, taskName, function, parameters...);
}
#else
/**
* \brief Measures the time of arbitrary function calls.
*
* This lambda works with C++14 and it accepts universal references.
*
* \param enabled whether time measurement should be enabled
* \param taskName name for printing the measured time
* \param function function to measure
* \param parameters function arguments
*
* \returns return value from given function
*/
auto measure = [](bool enabled, const std::string& taskName, auto&& function,
auto&&... parameters) -> decltype(auto)
{
std::chrono::steady_clock::time_point startTimePoint;
if (enabled)
{
startTimePoint = std::chrono::steady_clock::now();
}
decltype(auto) returnValue =
std::forward<decltype(function)>(function)(
std::forward<decltype(parameters)>(parameters)...);
if (enabled)
{
const std::chrono::steady_clock::time_point stopTimePoint =
std::chrono::steady_clock::now();
const std::chrono::duration<double> timeSpan =
std::chrono::duration_cast<std::chrono::duration<double>>(
stopTimePoint - startTimePoint);
std::cout << taskName << " took " << timeSpan.count() << " seconds." <<
std::endl;
}
return returnValue;
};
#endif
int main(int, char**)
{
measure(true, "Populating Ordered Set", []()
{
std::set<int> orderedSet;
for (int i = 0; i < 1000; ++i)
{
orderedSet.insert(i);
}
});
return 0;
}
如果它是用 C++11 编译器编译的(比如 g++ 和 -std=gnu++11),它使用模板函数,因此在这里工作得很好。如果它是使用 C++14 编译器 (-std=gnu++14) 编译的,它会使用 lambda,因此我会收到此编译错误消息:
..\src\Main.cpp: In instantiation of '<lambda(bool, const string&, auto:1&&, auto:2&& ...)> [with auto:1 = main(int, char**)::<lambda()>; auto:2 = {}; std::__cxx11::string = std::__cxx11::basic_string<char>]':
..\src\Main.cpp:186:10: required from here
..\src\Main.cpp:154:24: error: 'void returnValue' has incomplete type
decltype(auto) returnValue =
^~~~~~~~~~~
非常感谢您的帮助。
if it is compiled with a C++14 compiler (-std=gnu++14), it uses the lambda and therefore I get this compilation error message
让我用下面的伪代码简化你的函数
auto measure = [](someArguments....) -> decltype(auto)
{
something1();
decltype(auto) returnValue = somethingThatCanReturnVoid();
something2();
return returnValue;
};
问题出在 somethingThatCanReturnVoid()
return void
因为你不能定义 void
变量。
您可以使用以下事实
(1) 你不能定义一个 void
变量但是你可以写 return foo();
其中 foo()
是一个函数 returning void
(2) 如果你写return foo()
,函数范围内对象的销毁在foo()
在这一点上,解决方案似乎很明显:创建一个 Bar()
类型的对象并在 Bar
析构函数中执行 something2()
。
如下(伪代码)
auto measure = [](someArguments....) -> decltype(auto)
{
Bar b{otherArguments...}; // something1() in contruction;
// something2() in destruction;
return somethingThatCanReturnVoid();
};
这样,something1()
在somethingThatCanReturnVoid()
之前执行,something2()
在somethingThatCanReturnVoid()
之后执行,编译器不会抱怨
return somethingThatCanReturnVoid();
当 somethingThatCanReturnVoid()
return void
使用 RAII 代替您的计时:
struct Timer
{
explicit Timer(bool enable) : enable(enable)
{
if (enabled)
{
startTimePoint = std::chrono::steady_clock::now();
}
}
~Timer()
{
if (enabled)
{
const std::chrono::steady_clock::time_point stopTimePoint =
std::chrono::steady_clock::now();
const std::chrono::duration<double> timeSpan =
std::chrono::duration_cast<std::chrono::duration<double>>(
stopTimePoint - startTimePoint);
std::cout << taskName << " took " << timeSpan.count() << " seconds." <<
std::endl;
}
}
Timer(const Timer&) = delete;
Timer& operator=(const Timer&) = delete;
bool enable;
std::chrono::steady_clock::time_point startTimePoint;
};
然后你的函数变成:
template <typename Function, typename... Args>
auto measure(bool enabled, const std::string& taskName, Function&& function, Args&&... args)
-> decltype(std::forward<Function>(function)(std::forward<Args>(args)...))
{
Timer timer(enabled);
return std::forward<Function>(function)(std::forward<Args>(args)...);
}
有了使用 RAII 的想法,也可以简化模板代码。对于那些可能觉得方便的人,我想透露一下我的最终版本:
#include <chrono>
#include <iostream>
#include <set>
#include <boost/config.hpp>
/**
* \brief Internal timer that can be used to measure time with RAII.
*/
class InternalTimer
{
public:
/**
* \brief Instance creation starts the timer.
*
* \param enabled whether time measurement should be enabled
* \param taskName name for printing the measured time
*/
explicit InternalTimer(bool enabled, const std::string& taskName) :
enabled(enabled), taskName(taskName)
{
if (enabled)
{
startTimePoint = std::chrono::steady_clock::now();
}
}
/**
* \brief Destructing the instance stops the timer and prints the measurement.
*/
~InternalTimer()
{
if (enabled)
{
const std::chrono::steady_clock::time_point stopTimePoint =
std::chrono::steady_clock::now();
const std::chrono::duration<double> timeSpan = std::chrono::duration_cast<
std::chrono::duration<double>>(stopTimePoint - startTimePoint);
std::cout << taskName << " took " << timeSpan.count() << " seconds."
<< std::endl;
}
}
/**
* \brief Deleted copy constructor.
*/
InternalTimer(const InternalTimer&) = delete;
/**
* \brief Deleted assignment operator.
*
* \returns reference to the object that is assigned to
*/
InternalTimer& operator=(const InternalTimer&) = delete;
private:
bool enabled;
const std::string& taskName;
std::chrono::steady_clock::time_point startTimePoint;
};
#ifdef BOOST_NO_CXX14_GENERIC_LAMBDAS
/**
* \brief Measures the time of arbitrary function calls.
*
* This template function works with C++11 and therefore it does not use
* universal references.
*
* \tparam Function function type
* \tparam Parameters parameters type
* \param enabled whether time measurement should be enabled
* \param taskName name for printing the measured time
* \param function function to measure
* \param parameters function arguments
*
* \returns return value from given function
*/
template <typename Function, typename... Parameters>
auto measure(bool enabled, const std::string& taskName, Function function,
Parameters... parameters) -> decltype(function(parameters...))
{
InternalTimer timer(enabled, taskName);
return std::forward<Function>(function)(
std::forward<Parameters>(parameters)...);
}
#else
/**
* \brief Measures the time of arbitrary function calls.
*
* This lambda works with C++14 and it accepts universal references.
*
* \param enabled whether time measurement should be enabled
* \param taskName name for printing the measured time
* \param function function to measure
* \param parameters function arguments
*
* \returns return value from given function
*/
auto measure = [](bool enabled, const std::string& taskName, auto&& function,
auto&&... parameters) -> decltype(auto)
{
InternalTimer timer(enabled, taskName);
return std::forward<decltype(function)>(function)(
std::forward<decltype(parameters)>(parameters)...);
};
#endif
int main(int, char**)
{
measure(true, "Populating Ordered Set", []()
{
std::set<int> orderedSet;
for (int i = 0; i < 1000; ++i)
{
orderedSet.insert(i);
}
});
return 0;
}