c++templatestemplate-classes

Partial specialization tempate c++


I was exploring the JUCE framework and find this interesting code,

namespace SampleTypeHelpers // Internal classes needed for handling sample type classes
{
    template <typename T, bool = std::is_floating_point<T>::value>
    struct ElementType
    {
        using Type = T;
    };

    template <typename T>
    struct ElementType<T, false>
    {
        using Type = typename T::value_type;
    };
}

I've found out reading a little bit that, this is called partial specialization in c++, and I think it's pretty clear, but there is a thing i do not understand, in the first template there is that bool initialized, what is doing?

Thanks to everyone that will be answering!


Solution

  • Look at this function declaration:

    void frob(float f, bool g = true) {
        // ...
    }
    

    As you might know, you can drop parameter name from declarations:

    void frob(float f, bool = true) {
        // ...
    }
    

    You can also set the default value using an expression. This is equivalent as the previous example:

    void frob(float f, bool = std::is_floating_point<float>::value) {
        // ...
    }
    

    Then, let's add a simple template for the sake of the exercise:

    template<typename T>
    void frob(T f, bool = std::is_floating_point<T>::value) {
        // ...
    }
    

    Technically, you could move parameters to be template parameters, if you know them at compile time:

    template<typename T, bool = std::is_floating_point<T>::value>
    void frob(T f) {
        // ...
    }
    

    Now it's beginning to look like your own template declaration isn't it?


    Now that you understand the syntax, let's see how it's useful with partial specialization.

    Imagine you're the compiler and you expand the template instantiation of ElementType for float and std::integral_constant<int, 0>:

    template<>
    struct ElementType<float, std::is_floating_point<float>::value> { ... };
    
    template<>
    struct ElementType<
        std::integral_constant<int, 0>,
        std::is_floating_point<int>::value
    > { ... };
    

    Expands the constants, you get this:

    template<>
    struct ElementType<float, true> { ... };
    
    template<>
    struct ElementType<std::integral_constant<int, 0>, false> { ... };
    

    Now that you expanded this, you see that you have both true and false as the second parameter. You pick the specialization for each:

    template<>
    struct ElementType<float, true> { using Type = float; };
    
    template<>
    struct ElementType<std::integral_constant<int, 0>, false> {
        // int
        using Type = typename std::integral_constant<int, 0>::value_type;
    };