Prior to C++17, when writing a template non-type parameter, you had to specify its type first. So a common pattern became writing something like:
template <class T, T N>
struct integral_constant {
using type = T;
static constexpr T value = N;
};
using five = integral_constant<int, 5>;
But for complicated expressions, using something like this involves having to write decltype(expr), expr
when instantiating templates. The solution is to simplify this idiom and simply allow auto
:
template <auto N>
struct integral_constant {
using type = decltype(N);
static constexpr type value = N;
};
using five = integral_constant<5>;
A nice motivating example can come from trying to combine the empty base optimization with a custom deleter for unique_ptr
. Different C API deleters have different return types, but we don't care - we just want something to work for any function:
template <auto DeleteFn>
struct FunctionDeleter {
template <class T>
void operator()(T* ptr) const {
DeleteFn(ptr);
}
};
template <T, auto DeleteFn>
using unique_ptr_deleter = std::unique_ptr<T, FunctionDeleter<DeleteFn>>;
And now you can simply use any function pointer that can take an argument of type T
as a template non-type parameter, regardless of return type, and get a no-size overhead unique_ptr
out of it:
unique_ptr_deleter<std::FILE, std::fclose> p;