Поскольку вы используете C ++ 17, функция noexcept
функции является частью ее типа.
Чтобы проверить тип аргументов и тип возвращаемого значения для одного и того же типа, вы можетепросто используйте простое 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)>);
Вы также можете использовать вычет аргумента шаблона, чтобы увидеть, не является ли тип функции исключением, без проверки 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));
.Полное решение довольно долго работает для const
(и других) квалифицированных функций-членов, а также для функций с переменными параметрами:
#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));
(Большую часть времени вы можете обойтись просто поддержкой RetT(Args...)
, RetT(T::*)(Args...)
и RetT(T::*)(Args...) const
, так как вы редко видите дикие функции и функции-члены категории значений в дикой природе)
Это не будет работать с шаблонными или перегруженными функциями / функциями-членами.Это связано с тем, что noexcept-ness может зависеть от параметров шаблона или перегруженных типов аргументов.Вы можете вручную указать аргументы шаблона (например, is_noexcept(add<int>)
и !is_noexcept(add<std::string>)
) или использовать операторы noexcept
и std::declval
.
.