如何检测模板参数是否为noexcept函数?

10
我有一个函数,用于生成一个 lambda 函数来包装一个我之后可以调用的函数。
template <typename F, typename... FArgs>
auto make_lambda( F&& f, FArgs&&... f_args )
{
    return [&] () -> std::result_of_t<F( FArgs... )>
    {
        return std::forward<F>( f )( std::forward<FArgs>( f_args )... );
    };
}

如果参数fnoexcept,我希望返回的lambda表达式也是noexcept的,这样我的函数返回值将会是这个样子:

return [&] () noexcept( is_noexcept<decltype( f )>::value )
    -> std::result_of_t<F( FArgs... )>
{
    return std::forward<F>( f )( std::forward<FArgs>( f_args )... );
};

我的尝试:

#include <type_traits>

void f() {}
void g() noexcept {}

template <typename F, typename... Args>
struct is_noexcept : std::false_type {};

template <typename F, typename... Args>
struct is_noexcept<F( Args... ) noexcept> : std::true_type {};

int main()
{
    bool constexpr func_test_a{ is_noexcept<decltype( f )>::value }; // true
    bool constexpr func_test_b{ is_noexcept<decltype( g )>::value }; // true
}

然而,测试始终返回true。我错过了什么?是否有人能提供解决此问题的方法?

2个回答

13

来自:http://en.cppreference.com/w/cpp/language/noexcept_spec

noexcept说明符不是函数类型的一部分。(直到 C++17)

目前,由于noexcept说明符不是函数类型的一部分,模板推导将无法产生正确的结果。仅在C++17后,模板类型推导才能正常工作。我检测函数是否为noexcept的方法将在C++17中依然有效,同时这个答案的方法也是如此。


11
你可以使用noexcept操作符,它接受一个表达式并在该表达式为noexcept时生成true
未经测试,但这可能适用于您的使用情况。
return [&] () noexcept(noexcept(std::forward<F>( f )( std::forward<FArgs>( f_args )... )))
    -> std::result_of_t<F( FArgs... )>
{
    return std::forward<F>( f )( std::forward<FArgs>( f_args )... );
};

那是我尝试的第一件事,但它仍然对f()g()返回true - user2296177
1
正如我在答案中所指出的,这将是C++17中的有效答案。 - user2296177
经过严格测试,使用gcc 7.3.1和clang 5.0.2。在C++11或C++14中,对于任何编译时实例化类型(std::declvalstd::forward或简单的f),而不是表达式,使用noexcept总是会导致结果为false,甚至会有gcc的警告。再次强调,经过了严格的测试。奇怪的是,我在/usr/include/c++/type_traits中发现的std::is_nothrow_destructible实现依赖于类似于std::integral_constant<bool, noexcept(declval<T>().~T())>的东西,但我认为这在表达式方面是一个灰色区域(仍然很困惑)。 - mr.stobbe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接