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>;

Empty custom deleter for std::unique_ptr

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;