A Workaround for Lambda ODR Violations






1.11/5 (6 votes)
As brought up in the a reddit post with lambdas in inline functions you can run into ODR violations and thus undefined behavior.
As brought up in the post https://www.reddit.com/r/cpp/comments/40lm8o/lambdas_are_dangerous/ with lambdas in inline functions you can run into ODR violations and thus undefined behavior.
There is also a stack overflow discussion at http://stackoverflow.com/questions/34717823/does-using-lambda-in-header-file-violate-odr
While, the ultimate fix may rely with the Core Working Group, I think here is a work around.
The basis for the trick come from Paul Fultz II in a post about constexpr lambda. You can find the post at http://pfultz2.com/blog/2014/09/02/static-lambda/
Here is some problematic code from the stackoverflow discussion. The lambda may have a different type across translation units and thus result in different specializations of for_each being called for different translation units resulting in ODR violations and thus undefined behavior.
Also note that because T is defaulted, g can be used without any changes from before.
ideone at https://ideone.com/NdBpXN
There is also a stack overflow discussion at http://stackoverflow.com/questions/34717823/does-using-lambda-in-header-file-violate-odr
While, the ultimate fix may rely with the Core Working Group, I think here is a work around.
The basis for the trick come from Paul Fultz II in a post about constexpr lambda. You can find the post at http://pfultz2.com/blog/2014/09/02/static-lambda/
Here is some problematic code from the stackoverflow discussion. The lambda may have a different type across translation units and thus result in different specializations of for_each being called for different translation units resulting in ODR violations and thus undefined behavior.
inline void g() { int arr[2] = {}; std::for_each(arr, arr+2, [] (int i) {std::cout << i << ' ';}); }Here is a simple fix that will prevent the ODR violation.
// Based on Richard Smith trick for constexpr lambda // via Paul Fultz II (http://pfultz2.com/blog/2014/09/02/static-lambda/) template<typename T> auto addr(T &&t) { return &t; } static const constexpr auto odr_helper = true ? nullptr : addr([](){}); template <class T = decltype(odr_helper)> inline void g() { int arr[2] = {}; std::for_each(arr, arr+2, [] (int i) {std::cout << i << ' ';}); }We create a static const constexpr null pointer with the type of a lambda. If lambdas are different types across different translation units then
odr_helper
will have different types across different translation units. Because g now is a template function using the type of odr_helper
, g will be a different specialization across different translation units and thus will not result in an odr violation.Also note that because T is defaulted, g can be used without any changes from before.
ideone at https://ideone.com/NdBpXN