What does "::type=0" in first template mean?

Viewed 182

The following is part of the code:

// Null-delimited strings, and the like.

template < typename CharT,
           typename std::enable_if <
               std::is_pointer<CharT>::value &&
               !std::is_array<CharT>::value &&
               std::is_integral<typename std::remove_pointer<CharT>::type>::value &&
               sizeof(typename std::remove_pointer<CharT>::type) == 1,
               int >::type = 0 >
contiguous_bytes_input_adapter input_adapter(CharT b)
{
    auto length = std::strlen(reinterpret_cast<const char*>(b));
    const auto* ptr = reinterpret_cast<const char*>(b);
    return input_adapter(ptr, ptr + length);
}

template<typename InputType>
JSON_HEDLEY_WARN_UNUSED_RESULT
static basic_json parse(InputType&& i,
                        const parser_callback_t cb = nullptr,
                        const bool allow_exceptions = true,
                        const bool ignore_comments = false)
{
    basic_json result;
    parser(detail::input_adapter(std::forward<InputType>(i)), cb, allow_exceptions, ignore_comments).parse(true, result);
    return result;
}

static const char *g_sJsonTextInput = "{"
"  \"_nested\": {"
"    \"_bool\": false,"
"    \"_int\": 0,"
"    \"_double\": 0,"
"    \"_string\": \"foo\""
"  }"
"}";

parse(g_sJsonTextInput);

I think the result of "std::enable_if<...>::type" is the first template parameter, is that correct? If it’s right, how to understand "::type = 0"?

Please help, Thanks!

3 Answers

typename std::enable_if<...>::type is the type of the second template parameter. That parameter is unnamed. It has a default argument = 0, which is used if you don't specify any argument for it (which is the intent here).

the result of "std::enable_if<...>::type" is the first template parameter

It's either the second parameter of enable_if if the condition is true, or an invalid type if the condition is false. It being invalid would trigger SFINAE and disable this function for a specific CharT type.

In modern C++, you would use a shorter notation:

  • std::enable_if_t<...> (without typename and ::type).
  • std::is_..._v<...> (instead of std::is_...<...>::value).

Also, instead of int =0 you should use std::nullptr_t =nullptr. With an int, user can inadvertently create several different instantiations of your template by passing different integers to it, which is impossible with std::nullptr_t because it only has one possible value.

Or, if you use C++20, you should use the requires notation.

This is SFINAE (substitution failure is not an error) template stuff. If we look at enable_if, then it says:

template< bool B, class T = void >
struct enable_if;

If B is true, std::enable_if has a public member typedef type, equal to T; otherwise, there is no member typedef.

So if the big boolean expression evaluates to true, the enable_if expression substitutes to basically int = 0 which is ok, but if it evaluates to false it substitutes to = 0 which fails to compile - but only results in a compilation error if all other possible substitutions also fail (per the SFINAE name).

std::enable_if<...>::type is a hack of the template resolution system.

The SFINAE principle states that if a template resolution is not possible for a function call, the compilation should not fail, but simply try the next (and possibly worse) template resolution.

std::enable_if is defined in such a way that if it's first argument evaluates to false, the ::type is not defined.

to break it down:

template <typename Something,
    typename int = 0>
void function() {}

is valid C++.

So is std::enable_if<condition_evaluating_to_true, SomeType>::type (and the whole expression evaluates to SomeType).

std::enable_if<condition_evaluating_to_false, SomeType>::type is not a valid expression.

std::enable_if<...>::type = 0 roughly means "only apply the template resolution containing std::enable_if when the condition within enable_if evaluates to true".

Related