将类似“函数指针”的成员设置为 noexcept lambda

问题描述 投票:0回答:1

我无法达到标题中的效果,如以下代码片段所示:

#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

在C++14中,msvc拒绝函数指针版本:

错误 C2440:'=':无法从 'Dummy::InitFuns::' 转换为 'void (__cdecl *)(void) noexcept'

定义了

USESTDFUN

在 C++17 中
gcc
clang
msvc
拒绝
std::function
版本:
gcc

错误:字段“Bar”的类型“std::function”不完整 13 | std::function 酒吧;

msvc
: 错误 C2338:static_assert 失败:“std::function 不接受 noexcept 函数类型作为模板参数。”

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 不能衰减为函数指针成员,但我不明白为什么以及它们是否是实现此功能的解决方法。

c++ function-pointers member std-function
1个回答
1
投票

有一种解决方法,可以让您在将 lambda 分配给 std::function 之前检测它是否为 noexcept 。 另一种选择是在任何 noexcept 函数周围包装一个 try/catch 块(运行时检测,因此不是最理想的)

示例如下:

#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;
}
© www.soinside.com 2019 - 2024. All rights reserved.