Search code examples
c++language-designtype-traits

Why does `std::integral_constant` have a `::type` that refers to itself?


I'm reading the documentation on std::integral_constant, and apparently it includes using type that refers to itself.

template <typename T, T Value>
struct integral_constant
{
    using type = integral_constant<T, Value>;
    // ...
};

What is this typedef used for?

To access it you already need to know the type, so I don't see what benefit it brings.


Solution

  • From N1424:

    An important use case for providing a type representation for the result of Boolean traits comes from the template metaprogramming, the discipline where class templates are treated as compile-time invocable entities (metafunctions), and where experience has shown that to build a consistent and coherent framework of well-interpolating components it's crucially important for the metafunctions to provide a single common form of invocation [11]. Since it is possible to wrap an integral constant in a type, but not vice-versa, the implementation of boost type traits library was re-written to provide the result of Boolean trait templates through both their static const bool value member and the nested type typedef exposing the corresponding specialization of an integral constant type wrapper.

    So if you had some overloaded function like:

    void do_something(std::true_type);
    void do_something(std::false_type);
    

    For any "Boolean trait", you can always use do_something(typename Trait<T>::type{}).

    This is especially useful when Trait is built up from other template metaprogramming, which might only give a type member but not a value (because it was meant for types), e.g.:

    template<typename Trait, typename T>
    void call_do_something() {
        do_something(typename Trait<T>::type{});
    }
    
    int main() {
        using namespace mp = ::metaprogramming_library;
        using is_single_dim_array = mp::partial<mp::ic_equal, std::integral_constant<std::size_t, 1>, std::rank>;
        // partial only has a member type because it's meant for type tratis
        call_do_something<is_single_dim_array::type, int[1]>();
        call_do_something<is_single_dim_array::type, int[1][2]>();
    }