Тестирование на negative feature
, следовательно, гарантия того, что определенная конструкция не сможет скомпилироваться, возможно с использованием выражений c++20
requires
следующим образом:
Простой пример
Ниже я проверяю, существуют ли перегрузки функции func
в статических утверждениях, при использовании с тестовой средой логическое значение должно использоваться в одном из тестов времени выполнения, чтобы не блокировать компиляцию других тестов:
#include <concepts>
/// Arbitrary restrictions in order to test:
/// if T != double -> zero args
template <typename T> void func(){};
/// if T == double -> arbitrary args.
template<std::same_as<double> ...T> void func(T... as){};
template <typename T, typename... a> constexpr bool applies_to_func = requires(a... as) {
func<T>(as...);
};
/// compiles:
static_assert(applies_to_func<int>);
static_assert(applies_to_func<double, double>);
static_assert(applies_to_func<double, double, double>);
/// function fails to compile:
static_assert(!applies_to_func<int, int>);
Код доступен в Compiler Explorer: https://godbolt.org/z/direWo.
C++17
Недавно я попытался сделать нечто подобное для проекта, в котором я могу использовать только С++ 17. В моем коде я также проверяю, соответствует ли возвращаемый тип функции ожиданиям вызывающей стороны. Помимо некоторых ограничений, касающихся нетиповых параметров шаблона, можно добиться аналогичного результата, как показано ниже. В этом случае я не мог использовать double в качестве входных данных для перегрузки из-за неявного преобразования, applies_to_func(void, int, int)
будет оцениваться как true в приведенном ниже фрагменте кода.
#include <utility>
#include <string>
/// Create compile-time tests that allow checking a specific function's type
#define COMPILE_TIME_TEST(func) COMPILE_TIME_TEST_FUNCTION(func, func)
#define COMPILE_TIME_TEST_FUNCTION(name, func) \
namespace detail { \
template<typename R, auto... args> struct name ## FromArgs:std::false_type{}; \
template<auto... args> struct name ## FromArgs<decltype(func(args...)), args...> : std::true_type{}; \
template<typename R, typename... Args> struct name ## FromType:std::false_type{}; \
template<typename... Args> struct name ## FromType<decltype(func(std::declval<Args>()...)), Args...> : std::true_type{};\
} \
template<typename R, auto ...Args> \
static constexpr auto name ## _compiles = detail::name ## FromArgs<R, Args...>::value; \
template<typename ...Args> \
static constexpr auto name ## _compiles_from_type = detail::name ## FromType<Args...>::value;\
int func();
template <typename T> void func(T);
void func(double);
void func(double, double );
void func(double, double, double);
// create the structs from above macros for the function `func`
COMPILE_TIME_TEST(func);
static_assert(!func_compiles<void>);
static_assert(func_compiles<int>);
static_assert(func_compiles_from_type<void, double, double>);
static_assert(!func_compiles_from_type<void, double, double, double, double>);
static_assert(func_compiles<void, 1>);
static_assert(!func_compiles<void, 1, nullptr>);
26.03.2020