0

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!

cedrata
  • 29
  • 7
  • That's a default argument, used when the instantiation omits an explicit argument. E.g. `ElementType` is a shorthand for `ElementType::value>` – Igor Tandetnik Jan 30 '22 at 20:53

2 Answers2

1

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;
};
Guillaume Racicot
  • 39,621
  • 9
  • 77
  • 141
0

Just like a functions can take default argument, template can have default parameters.

int foo(int a = 5) { ... }

foo(7) // a = 7
foo()  // a = 5

The equivalent template example would be

template <int I = 5>
int bar() { ... }

bar<10>() // I = 10
bar()     // I = 5

In your example the name for the parameter was also left out, which is a common practice if the template parameter is only used to select a partial specialization. We don't need it's name since we never refer to it. We just need it to have a certain value/type.

super
  • 12,335
  • 2
  • 19
  • 29