我无法达到标题中的效果,如以下代码片段所示:
#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;
};};
直播
定义了
USEFUNPTR
:错误 C2440:'=':无法从 'Dummy::InitFuns::
' 转换为 'void (__cdecl *)(void) noexcept'
定义了
USESTDFUN
:gcc
、clang
和 msvc
拒绝 std::function
版本:gcc
:
错误:字段“Bar”的类型“std::function
”不完整 13 | std::function 酒吧; : 错误 C2338:static_assert 失败:“std::function 不接受 noexcept 函数类型作为模板参数。”msvc
msvc
更明确,表明从 C++17 开始,std::function
似乎无法包装 noexcept
函数(确实没有 std::function
与 noexcept
的原型,并且此说明符是如果我理解正确的话(来自其他一些 SO 帖子),请从 C++17 输入:
错误 C2440:'=':无法从 'Dummy::InitFuns::
' 转换为 'void (__cdecl *)(void) noexcept'
如何以适用于所有 C++>=14 和所有编译器的方式实现此功能?
(在实际用例中,InitFuns 可能具有运行时和/或编译时参数,并且 lambda 定义将取决于这些参数,但调用
Foo
或 Bar
的代码不知道这些参数)。
注意,我发现几篇文章表明 lambda 不能衰减为函数指针成员,但我不明白为什么以及它们是否是实现此功能的解决方法。
有一种解决方法,可以让您在将 lambda 分配给 std::function 之前检测它是否为 noexcept
示例如下:
#include <functional>
#include <type_traits>
#include <iostream>
#include <stdexcept>
// noexcept will not be picked up by std::function
// but with SFINAE you can detect the noexcept
namespace traits
{
template<typename fn_t>
using is_noexcept_fn = std::enable_if_t<noexcept(std::declval<fn_t>()()), fn_t>;
}
class Dummy
{
public:
// Only accept the lambda (function object) if it has a noexcept operator()
template<typename fn_t, typename enable_t = traits::is_noexcept_fn<fn_t>>
explicit Dummy(fn_t fn) :
m_fn{ fn }
{
}
void operator()()
{
m_fn();
}
private:
std::function<void()> m_fn;
};
class Dummy2
{
public:
// other option is to actually catch all exceptions for
// functions that are not noexcept
template<typename fn_t>
explicit Dummy2(fn_t fn)
{
if constexpr (noexcept(fn()))
{
m_fn = fn;
}
else
{
auto lambda = [=]()
{
try
{
fn();
}
catch (...)
{
std::cout << "unhandled exeption caught\n";
}
};
m_fn = lambda;
}
}
void operator()()
{
m_fn();
}
private:
std::function<void()> m_fn;
};
int main()
{
//Dummy d1{ [] { std::cout << "does not compile\n"; } };
Dummy d{ []() noexcept { std::cout << "does compile\n"; } };
d();
Dummy2 d2{ [] { throw std::runtime_error{"oops"}; } };
d2();
return 0;
}