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)
CodePudding user response:
If you have access to c 17 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...
}
}
CodePudding user response:
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;
}
CodePudding user response:
I also tried a non-templated overload though it always picks the first version...
How did you try non-templated overloads? All you need is to define another overload of the template function for your type:
#include <iostream>
template <typename Type, typename... Arguments>
void function(Type, Arguments&&...)
{ std::cout << __PRETTY_FUNCTION__ << std::endl; }
void function(double const&)
{ std::cout << __PRETTY_FUNCTION__ << std::endl; }
void function(std::string const&)
{ std::cout << __PRETTY_FUNCTION__ << std::endl; }
struct Icon {};
void function(Icon const&)
{ std::cout << __PRETTY_FUNCTION__ << std::endl; }
void function(Icon* const)
{ std::cout << __PRETTY_FUNCTION__ << std::endl; }
int main()
{
function("A", "B", 3.14, 42);
function(1.2);
function(std::to_string(1.2));
function(Icon());
Icon icon;
function(icon);
function(std::move(icon));
function(&icon);
}
In godbolt: godbolt.org/z/bjfbM9Wqs