Since you are using C++17, the noexcept
-ness of a function is part of its type.
To check the type of the arguments and return type at the same type, you can just use a simple std::is_same
:
void foo(int, int) noexcept;
void bar(long, float);
struct S {
void foo(int, int) noexcept;
void bar(long, float);
};
static_assert(std::is_same_v<decltype(foo), void(int, int) noexcept>);
static_assert(std::is_same_v<decltype(bar), void(long, float)>);
static_assert(std::is_same_v<decltype(&S::foo), void(S::*)(int, int) noexcept>);
static_assert(std::is_same_v<decltype(&S::bar), void(S::*)(long, float)>);
You can also use template argument deduction to see if a function type is noexcept without having to check noexcept(std::declval<S>().foo(std::declval<arg_1_t>(), std::declval<arg_2_t>()))
:
// Arguments, return type and type of the class that has the member function are
// all deduced, but this overload is only called if noexcept
template<class RetT, class T, class... Args>
constexpr bool is_noexcept_function(RetT(T::*)(Args...) noexcept) {
return true;
}
// And this one is called if not noexcept
template<class RetT, class T, class... Args>
constexpr bool is_noexcept_function(RetT(T::*)(Args...)) {
return false;
}
static_assert(is_noexcept_function(&S::foo));
static_assert(!is_noexcept_function(&S::bar));
The full solution is kind of long to work for const
(and other) qualified member functions, as well as functions with variadic parameters:
#include <type_traits>
// Check if a regular function is noexcept
template<class Ret, class... Args>
constexpr std::false_type is_noexcept_function(Ret(Args...)) noexcept {
return {};
}
template<class Ret, class... Args>
constexpr std::true_type is_noexcept_function(Ret(Args...) noexcept) noexcept {
return {};
}
// Check if a regular function with C-style variadic arguments is noexcept
template<class Ret, class... Args, bool is_noexcept>
constexpr std::false_type is_noexcept_function(Ret(Args......)) noexcept {
return {};
}
template<class Ret, class... Args, bool is_noexcept>
constexpr std::true_type is_noexcept_function(Ret(Args......) noexcept) noexcept {
return {};
}
// Check if a member function is noexcept
#define DEFINE_IS_NOEXCEPT_FUNCTION_FOR_METHOD(QUALIFIER) \
template<class Ret, class T, class... Args> \
constexpr std::false_type is_noexcept_function(Ret(T::*)(Args...) QUALIFIER) noexcept { \
return {}; \
} \
template<class Ret, class T, class... Args> \
constexpr std::true_type is_noexcept_function(Ret(T::*)(Args...) QUALIFIER noexcept) noexcept { \
return {}; \
} \
template<class Ret, class T, class... Args, bool is_noexcept> \
constexpr std::false_type is_noexcept_function(Ret(T::*)(Args......) QUALIFIER) noexcept { \
return {}; \
} \
template<class Ret, class T, class... Args, bool is_noexcept> \
constexpr std::true_type is_noexcept_function(Ret(T::*)(Args......) QUALIFIER noexcept) noexcept { \
return {}; \
}
#define DEFINE_IS_NOEXCEPT_FUNCTION_FOR_METHOD_VALUE_CLASS(VALUE_CLASS) \
DEFINE_IS_NOEXCEPT_FUNCTION_FOR_METHOD(VALUE_CLASS) \
DEFINE_IS_NOEXCEPT_FUNCTION_FOR_METHOD(const VALUE_CLASS) \
DEFINE_IS_NOEXCEPT_FUNCTION_FOR_METHOD(volatile VALUE_CLASS) \
DEFINE_IS_NOEXCEPT_FUNCTION_FOR_METHOD(const volatile VALUE_CLASS)
DEFINE_IS_NOEXCEPT_FUNCTION_FOR_METHOD_VALUE_CLASS()
DEFINE_IS_NOEXCEPT_FUNCTION_FOR_METHOD_VALUE_CLASS(&)
DEFINE_IS_NOEXCEPT_FUNCTION_FOR_METHOD_VALUE_CLASS(&&)
#undef DEFINE_IS_NOEXCEPT_FUNCTION_FOR_METHOD
#undef DEFINE_IS_NOEXCEPT_FUNCTION_FOR_METHOD_VALUE_CLASS
// Usage example
void foo(int, int) noexcept;
void bar(long, float);
struct S {
void foo(int, int) const noexcept;
void bar(long, float) &&;
};
static_assert(is_noexcept_function(foo));
static_assert(!is_noexcept_function(bar));
static_assert(is_noexcept_function(&S::foo));
static_assert(!is_noexcept_function(&S::bar));
(Most of the time you can get away with just supporting RetT(Args...)
, RetT(T::*)(Args...)
and RetT(T::*)(Args...) const
, as you rarely see variadic functions and value category qualified member functions in the wild)
This won't work with templated or overloaded functions/member functions. This is because the noexcept-ness might depend on the template parameters or the overloaded argument types. You can manually provide the template arguments (e.g. is_noexcept(add<int>)
and !is_noexcept(add<std::string>)
) or fall back to the noexcept
operator and std::declval
.
().foo())` in place of `noexcept(S().foo())` if `S` is not default constructible– alter_igel Jun 08 '19 at 22:12()` to be valid (and the operand expr of `noexcept` will be unevaluated). I.e., you may place `static_assert(noexcept(std::declval– dfrib Jun 08 '19 at 22:26().foo()))` in the definition of the class, say, immediately below the declaration(/definition) of `foo()`.