5
\$\begingroup\$

Today's question will just be about a small utility that I needed at some point for one of my projects: a template to detect whether a template is specialized for a given type. The utility uses std::void_t from C++17, but this alias template is simle enough to be implemented in you favourite C++ revision, whichever it is:

template<typename...>
struct voider
{
    using type = void;
};

template<typename... TT>
using void_t = typename voider<TT...>::type;

Here is the template utility I wrote to detect whether a template has a specialization for a given type, for SFINAE purpose:

template<
    template<typename...> class,
    typename,
    typename=void
>
struct is_specialized:
    std::false_type
{};

template<
    template<typename...> class Template,
    typename T
>
struct is_specialized<Template, T, std::void_t<decltype(Template<T>{})>>:
    std::true_type
{};

And here is a small example of how it can be used:

template<typename T>
struct foo;

template<>
struct foo<int> {};

template<>
struct foo<float> {};

int main()
{
    // Should print 1 1 0
    std::cout << is_specialized<foo, int>::value << ' '
              << is_specialized<foo, float>::value << ' '
              << is_specialized<foo, double>::value;
}

Is there any way I could improve this specialization trait? Any kind of review is welcome :)

\$\endgroup\$
  • \$\begingroup\$ You obviously built this for something. What is your use case that makes it valuable. static_assert that specific specializations are made? Asserting that the correct header file with specializations has been included? \$\endgroup\$ – Martin York Jul 27 '15 at 18:28
  • \$\begingroup\$ @LokiAstari That was for a library with identity elements. That was to say "if there is a left identity element for this type/operation pair, pick it, otherwise check whether there is a specialization for the general identity element", but I found a simpler alternative meanwhile. You know how it is: you develop a tool and then find the obvious solution that you needed but couldn't see until there. \$\endgroup\$ – Morwenn Jul 27 '15 at 21:30
3
\$\begingroup\$

I think there's an issue with wording here. What you're testing is if a given instantiation of a class template is default constructible. You're not testing if it's "specialized". You cannot check for that. How would you differentiate between:

template <typename T> struct foo { };
template <> struct foo<int> { };

// is foo<X> the primary or the specialization?

Back to your example - just because you cannot construct an object of a particular instantiation (e.g. foo<double>) doesn't mean that you can't name the type. You can do that just fine. That lets you simplify your trait down to:

template <typename, typename=void>
struct is_specialized : std::false_type
{};

template<typename T>
struct is_specialized<T, void_t<decltype(T{})>>
    : std::true_type
{};

Which you would use as is_specialized<foo<int>>::value (true) or is_specialized<foo<double>>::value (false).

But really, since we're just testing for constructibility, we should just use what's in the standard:

template <typename T>
using is_specialized = std::is_default_constructible<T>;
\$\endgroup\$
  • \$\begingroup\$ Ok, now I feel stupid, I could indeed have used is_default_constructible for my needs .______. \$\endgroup\$ – Morwenn Jul 27 '15 at 16:13
  • \$\begingroup\$ That's what I get for overthinking things. \$\endgroup\$ – Morwenn Jul 27 '15 at 16:13

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.