Variadic function with restricted types
struct has public inheritance

By default, struct has public inheritance. Therefore, you don't need to manually specify it every time. You can write shorter (and so more readable) code:

template &lt;&gt;
struct template_all&lt;&gt;
  : std::true_type {};

template &lt;typename ... Types&gt;
struct template_all&lt;std::false_type, Types...&gt;
  : std::false_type {};

template &lt;typename ... Types&gt;
struct template_all&lt;std::true_type, Types...&gt;
  : template_all&lt;Types...&gt;::type {};


Since type traits generally use many templates, you don't want to add even more extra keywords all over the place.

Use alias templates

If you have acces to a compiler that supports some C++14 features, you will probably want to use the alias templates for transformation traits to get rid of many typename and ::type. Unfortunately, the alias templates for the query traits (is_*) have not been accepted, so you have to use std::is_same.

template &lt;typename ... Args&gt;
auto link_complete(Args&amp;&amp; ... args) -&gt;
  std::enable_if_t&lt;
    template_all&lt;
      typename std::is_same&lt;
        std::remove_reference_t&lt;Args&gt;,
        Node
      &gt;...
    &gt;::value,
    void
  &gt;
{
    Node::link_complete( { &amp;args... } );
}


static_assert is great

Instead of using SFINAE for link_complete, you should use static_assert since your function does not have any overload. It will allow you to give a meaningful error message instead of a somewhat obscure SFINAE error message:

template &lt;typename ... Args&gt;
void link_complete(Args&amp;&amp; ... args)
{

    static_assert(
        template_all&lt;
            typename std::is_same&lt;
                std::remove_reference_t&lt;Args&gt;,
                Node
            &gt;::type...
        &gt;::value,
        "link_complete arguments must be of the type Node"
    );

    Node::link_complete( { &amp;args... } );
}


You may want to use std::decay

std::decay calls std::remove_reference and std::remove_cv internally. If needed, it also calls std::remove_extent if T is an array type and calls std::add_pointer if T is a function type. In short:


  This is the type conversion applied to all function arguments when passed by value.


That allows you to check for what we generally think of as "same types":

template &lt;typename ... Args&gt;
void link_complete(Args&amp;&amp; ... args)
{

    static_assert(
        template_all&lt;
            typename std::is_same&lt;
                std::decay_t&lt;Args&gt;,
                Node
            &gt;::type...
        &gt;::value,
        "link_complete arguments must be of the type Node"
    );

    Node::link_complete( { &amp;args... } );
}


Rethink template_all

All in all, your goal is to test boolean conditions on types. Since the query traits have a static constexpr bool value member, you may want to create a template_all that checks for true and false (like std::enable_if or std::conditional) instead of checking for std::true_type and std::false_type. That would make your intent clearer, but it would be more like a all_true template.

template &lt;bool...&gt;
struct all_true; // UNDEFINED

template &lt;&gt;
struct all_true&lt;&gt;
  : std::true_type {};

template &lt;bool... Conds&gt;
struct all_true&lt;false, Conds...&gt;
  : std::false_type {};

template &lt;bool... Conds&gt;
struct all_true&lt;true, Conds...&gt;
  : all_true&lt;Conds...&gt; {};

template &lt;typename ... Args&gt;
void link_complete(Args&amp;&amp; ...)
{
    static_assert(
        all_true&lt;
            std::is_same&lt;
                std::decay_t&lt;Args&gt;,
                Node
            &gt;::value...
        &gt;::value,
        "must have Node types"
    );

    Node::link_complete( { &amp;args... } );
}