Yes, you can use both constexpr
and consteval
with variadic functions and function templates. This combination is particularly powerful for creating flexible, compile-time computations. Let's explore this with some examples
constexpr
FunctionHere's a simple example of a variadic constexpr
function that sums its arguments:
#include <iostream>
constexpr int sum() { return 0; }
template<typename T, typename... Args>
constexpr int sum(T first, Args... args) {
return first + sum(args...);
}
int main() {
constexpr int result1 = sum(1, 2, 3, 4, 5);
std::cout << "Sum: " << result1 << '\n';
constexpr int result2 = sum(10, 20, 30);
std::cout << "Sum: " << result2 << '\n';
}
Sum: 15
Sum: 60
consteval
FunctionSimilarly, we can create a variadic consteval
 function:
#include <iostream>
consteval int product() { return 1; }
template<typename T, typename... Args>
consteval int product(T first, Args... args) {
return first * product(args...);
}
int main() {
constexpr int result1 = product(2, 3, 4);
std::cout << "Product: " << result1 << '\n';
constexpr int result2 = product(5, 10, 2);
std::cout << "Product: " << result2 << '\n';
}
Product: 24
Product: 100
Here's a more complex example that performs compile-time string concatenation:
#include <algorithm>
#include <array>
#include <iostream>
#include <string_view>
template <std::size_t... Lengths>
constexpr auto concat(
const std::array<
std::string_view, sizeof...(Lengths)>& strs) {
constexpr std::size_t total_length = (
Lengths + ... + 0);
std::array<char, total_length + 1> result{};
char* ptr = result.data();
for (auto& str : strs) {
std::copy_n(str.data(), str.length(), ptr);
ptr += str.length();
}
result[total_length] = '\0';
return result;
}
int main() {
constexpr auto result = concat<5, 7, 6>({
"Hello", " World!", " C++20"
});
std::cout << result.data();
}
Hello World! C++20
if constexpr
C++17 introduced if constexpr
, which works great with variadic templates:
#include <iostream>
#include <type_traits>
template<typename T>
constexpr auto to_string(T value) {
if constexpr (std::is_same_v<T, int>) {
return "int";
} else if constexpr (std::is_same_v<T, double>) {
return "double";
} else {
return "unknown";
}
}
template<typename... Args>
constexpr void print_types(Args... args) {
((std::cout << to_string(args) << ' '), ...);
std::cout << '\n';
}
int main() {
print_types(42, 3.14, 'a');
}
int double unknown
Variadic templates with constexpr
are often used in type traits:
#include <iostream>
#include <type_traits>
template <typename... Ts>
struct all_integral : std::true_type {};
template <typename T, typename... Ts>
struct all_integral<T, Ts...>
: std::bool_constant<std::is_integral_v<T>
&& all_integral<Ts...>::value> {};
template <typename... Ts>
constexpr bool all_integral_v
= all_integral<Ts...>::value;
int main() {
constexpr bool result1
= all_integral_v<int, char, long>;
std::cout << "All integral 1: "
<< std::boolalpha << result1 << '\n';
constexpr bool result2
= all_integral_v<int, double, char>;
std::cout << "All integral 2: "
<< std::boolalpha << result2 << '\n';
}
All integral 1: true
All integral 2: false
These examples demonstrate the power and flexibility of combining constexpr
/consteval
with variadic functions and templates. They allow for complex compile-time computations, type manipulations, and metaprogramming techniques.
When using these features, remember to keep your code readable and maintainable, and be aware of potential increases in compilation time for very complex compile-time computations.
Answers to questions are automatically generated and may not have been reviewed.
Learn how to implement functionality at compile-time using constexpr
and consteval