5

I have a template for a function that accepts at least one parameter and performs some formatting on the rest:

template <typename T, typename... ARGS>
void foo(T first, ARGS&&... args)
{
    // ...
}

I want it to do a different thing when the first parameter is of a specific type. I like the compiler to choose this particular version, where the rest of the variadic arguments are just ignored. So I tried template specialization:

template <>
void foo(const ICON* p)
{
    // ...
}

or:

template <typename T, typename... ARGS>
void foo(const ICON* p, ARGS&&... args)
{
   // ...
}

I also tried a non-templated overload, though it always picks the first version.

The caller code is supposed not to know about templates at all (legacy code, backwards-compatibility nonsense, etc.) So it relies on type deduction and calls like this:

MyClass fooMaker;
fooMaker.set("A", "B", 3.14, 42); // v1
fooMaker.set(pIcon);              // v2 (special)
JeJo
  • 30,635
  • 6
  • 49
  • 88
sorush-r
  • 10,490
  • 17
  • 89
  • 173
  • Could you show the code of caller? – Louis Go Jul 11 '22 at 06:42
  • 2
    What problem are you having with this code? What is the full error message? Please show a [mre]. You probably just need to remove `typename T` from your second version. Note that you can't partially specialise functions. – Alan Birtles Jul 11 '22 at 06:48
  • @AlanBirtles, no error message. it compiles fine, but the compiler picks up the general implementation when the user code (shown in the question) is called. – sorush-r Jul 11 '22 at 06:51

2 Answers2

7

If you have access to or later, using if constexpr you can retain the true statement in the foo, at compile time, as follows:

#include <type_traits> // std::is_pointer_v, std::is_same_v

template <typename T, typename... ARGS>
void foo(T first, ARGS&&... args)
{
    if constexpr (std::is_pointer_v<T> // is pointer check
        && std::is_same_v<T, ICON*>)   // is the type is of "ICON*"
    {
        // ICON impli...
    }
    else
    {
        // other impli...
    }
}

See online demo

JeJo
  • 30,635
  • 6
  • 49
  • 88
4

Since function templates can't be partially specialized, we can overload them as shown below to get the desired effect:

struct ICON{};
template <typename T, typename... ARGS>
void foo(T first, ARGS&&... args) // #1
{
    std::cout<<"primary version selected"<<std::endl;
}
//-------v--------------------->typename T removed from here
template<typename... ARGS>
void foo(ICON*, ARGS&&...args)// #2
{
    std::cout<<"overloaded version used"<<std::endl;
}
int main()
{
    foo(1,2, "some string literal"); //calls #1
    ICON i{};
    ICON *ptrI = &i;
    foo(ptrI);                       //calls #2                     
    
    return 0;
}

Working demo

Jason
  • 36,170
  • 5
  • 26
  • 60
  • 2
    Very interesting... This can't handle `const`, but I guess that's not a big deal: https://onlinegdb.com/lN0fl1-N1 – sorush-r Jul 11 '22 at 07:05
  • @sorush-r Yes as there was no requirement/restriction in the question for using `const` i didn't bother mention it. – Jason Jul 11 '22 at 07:06