Шаблонный constexpr метод с переменным числом параметров
Есть вот такой код
#include <iostream>
#include <type_traits>
template <typename T>
constexpr T ThereNegative(const T& arg) {
if (arg < 0) return arg;
return 0;
}
template <typename F, typename ...Rest>
constexpr std::common_type_t<F, Rest...> ThereNegative(const F& first_arg, const Rest&... args) {
if (first_arg < 0) return first_arg;
return ThereNegative(args...);
}
template <typename ...T>
constexpr bool foo(const T& ...args) {
static_assert(ThereNegative(args...) == 0, "Negative argument.");
return true;
}
int main()
{
//foo(2, 4lu, char(5), 0, 3);
constexpr auto AAA = ThereNegative(2, 4lu, char(5), 0, 3);
constexpr auto BBB = ThereNegative(2, 4lu, char(-5), 0, 3);
std::cout << AAA << std::endl;
std::cout << BBB << std::endl;
return 0;
}
Он прекрасно работает и выводит ожидаемый результат
0
4294967291 (эта та самая -5)
Но когда я снимаю комментарий со строки
foo(2, 4lu, char(5), 0, 3);
вылазит ошибка компиляции
18:20:31: Выполняются этапы для проекта test_static_assert...
18:20:31: Запускается: «C:\Qt\Tools\CMake_64\bin\cmake.exe» --build D:/QT_PRO/build-test_static_assert-Desktop_Qt_6_6_0_MinGW_64_bit-Debug --target all
[1/2 3.3/sec] Building CXX object CMakeFiles/test_static_assert.dir/main.cpp.obj
FAILED: CMakeFiles/test_static_assert.dir/main.cpp.obj
C:\Qt\Tools\mingw1120_64\bin\g++.exe -DQT_QML_DEBUG -g -std=gnu++17 -MD -MT CMakeFiles/test_static_assert.dir/main.cpp.obj -MF CMakeFiles\test_static_assert.dir\main.cpp.obj.d -o CMakeFiles/test_static_assert.dir/main.cpp.obj -c D:/QT_PRO/test_static_assert/main.cpp
D:/QT_PRO/test_static_assert/main.cpp: In instantiation of 'constexpr bool foo(const T& ...) [with T = {int, long unsigned int, char, int, int}]':
D:/QT_PRO/test_static_assert/main.cpp:24:31: required from here
D:/QT_PRO/test_static_assert/main.cpp:18:42: error: non-constant condition for static assertion
18 | static_assert(ThereNegative(args...) == 0, "Negative argument.");
| ~~~~~~~~~~~~~~~~~~~~~~~^~~~
D:/QT_PRO/test_static_assert/main.cpp:18:42: error: 'args#0' is not a constant expression
D:/QT_PRO/test_static_assert/main.cpp:18:42: error: 'args#1' is not a constant expression
D:/QT_PRO/test_static_assert/main.cpp:18:42: error: 'args#2' is not a constant expression
D:/QT_PRO/test_static_assert/main.cpp:18:42: error: 'args#3' is not a constant expression
D:/QT_PRO/test_static_assert/main.cpp:18:42: error: 'args#4' is not a constant expression
ninja: build stopped: subcommand failed.
18:20:32: Процесс «C:\Qt\Tools\CMake_64\bin\cmake.exe» завершился с кодом 1.
Ошибка при сборке/развёртывании проекта test_static_assert (комплект: Desktop Qt 6.6.0 MinGW 64-bit)
Во время выполнения этапа «Собрать»
18:20:32: Прошло времени: 00:00.
Что именно тут происходит? Почему внутри метода ThereNegative
constant expression не теряется, а внутри foo
она потерялась?
Ответы (2 шт):
Вроде бы, разобрался.
https://ru.cppreference.com/w/cpp/language/static_assert
static_assert
должен содержать bool-constexpr
или константные выражения типа bool
или преобразованное константное выражение типа bool
. Ни то ни другое в контексте аргументов метода не возможно.
Вот такой код не компилируется
#include <iostream>
constexpr bool ItsLike(bool val) {
static_assert(val, "It's not like that");
return true;
}
int main()
{
std::cout << std::boolalpha << ItsLike(0 == 0); << std::endl;
return 0;
}
Пока наиболее близкое решение, что я нашёл, предоставляет С++20.
В нём есть возможность создавать constexpr
методы, содержащие try
.
Немного костыльно, но лучше мне пока придумать не удалось
#include <iostream>
#include <type_traits>
template <typename T>
constexpr void TryArePositive(const T& arg) {
if (arg < 0) throw std::invalid_argument("Negative argument.");
}
template <typename F, typename ...Rest>
constexpr void TryArePositive(const F& first_arg, const Rest&... args) {
if (first_arg < 0) throw std::invalid_argument("Negative argument.");
TryArePositive(args...);
}
template <typename ...Ts>
constexpr bool ArePositive(const Ts&... args) {
try {
TryArePositive(args...);
return true;
} catch (...) {
return false;
}
}
template <typename ...T>
constexpr bool foo2(const T& ...args) {
return ArePositive(args...);
}
int main()
{
constexpr auto b1 = foo2(2, 4lu, char(5), 0, 3); // OK
constexpr auto b2 = foo2(2, 4lu, char(-5), 0, 3); // ERROR - как и хотелось
std::cout << std::boolalpha << b1 << std::endl;
return 0;
}
Спасибо за подсказку от @user7860670. Не знал, что можно делать template <auto x>
. Действительно, это подходит, если нужны действия чисто в compile-time.
template <auto arg>
constexpr void AssertArePositive() {
static_assert(arg >= 0, "Negative argument.");
}
template <auto first, auto... rest>
requires (sizeof...(rest) > 0)
constexpr void AssertArePositive() {
AssertArePositive<first>();
AssertArePositive<rest...>();
}
int main()
{
AssertArePositive<2, 4lu, char(5), 1, 3>();
AssertArePositive<2, 4lu, char(-5), 1, 3>();
return 0;
}
Добавлено:
Позору моему нет предела. Спасибо за подсказку от @user7860670. Не знал, что раскрывать пакеты можно еще проще.
template <auto... args>
constexpr void AssertArePositive() {
static_assert(((args >= 0) and ...), "Parameters must be positive.");
}
int main()
{
AssertArePositive<2, 4lu, char(5), 1, 3>();
AssertArePositive<-2, 4lu, char(5), 1, 3>();
return 0;
}