I cannot achieve the effect in title, as implemented in the following snippet:
#include <functional>
#include <iostream>
#define USEFUNPTR
#define USESTDFUN
class Dummy {
public:
#ifdef USEFUNPTR
void (*Foo)() noexcept = nullptr;
#endif
#ifdef USESTDFUN
std::function<void() noexcept> Bar;
#endif
void InitFuns() {
#ifdef USEFUNPTR
Foo = []() noexcept { std::cout << "Foo\n" << std::endl; };
#endif
#ifdef USESTDFUN
Bar = []() noexcept { std::cout << "Bar\n" << std::endl; };
#endif
};
~Dummy() noexcept = default;
};};
Live
with USEFUNPTR defined:
In C++14, msvc refuses the function pointer version:
error C2440: '=': cannot convert from 'Dummy::InitFuns::<lambda_b40e7171393910f4bba39b7be19bf362>' to 'void (__cdecl *)(void) noexcept'
with USESTDFUN defined:
In C++17 gcc, clang and msvc reject the std::function version:
gcc:
error: field 'Bar' has incomplete type 'std::function<void() noexcept>' 13 | std::function<void() noexcept> Bar;
msvc: error C2338: static_assert failed: 'std::function does not accept noexcept function types as template arguments.'
msvc is more explicit and indicates that, from C++17, std::function seems unable to wrap a noexcept function (there is indeed no prototype of std::function with noexcept and this specifier is part of the type from C++17, if I understood correctly (from some other SO post):
error C2440: '=': cannot convert from 'Dummy::InitFuns::<lambda_b40e7171393910f4bba39b7be19bf362>' to 'void (__cdecl *)(void) noexcept'
How can I achieve this functionality in a way that works in all C++>=14, and with all compilers?
(In an actual usecase, the InitFuns may have runtime and/or compile time parameters and the lambda definition will depend on these parameters, yet the code that will call Foo or Bar is unaware of these parameters).
NB I found several posts indicating that a lambda cannot decay to a function pointer member but I didn't understand why and if they were a workaround to achieve this functionnality.
There is a kind of workaround, that lets you detect if the lambda is noexcept before you assign it to a std::function<void()>. Another option is to wrap a try/catch block around any noexcept function (runtime detections, so suboptimal)
Example here :