简体   繁体   中英

“Simulate” Partial Function Template Specialization?

I know that partial template specialization does not hold for functions. But is there any way to achieve something similar? My use case is quite simple, but I'm not sure what's the best way to solve it.

Let's say I'm writing a function that takes in an STL container:

template<typename T>
void doSomething(T& container) {
    // do something here
}

Now, I want a different definition for this function when std::forward_list and std::list are passed in.

At first I thought perhaps I can overload the function like this:

// overload for std::list
template<typename T>
void insertionCppStl(std::list<T>& container) {
    // do seomthing here for list
}

But that would mean I need to do the same overload for std::forward_list as well. Which is not ideal at all.

Is there any way to overload a function for two different input types? Looking for answers for C++17, but C++20 answer is welcomed as well.

Not a great solution but...

template <template <typename...> class C, typename T>
std::enable_if_t<std::is_same_v<C<T>, std::list<T>>
              || std::is_same_v<C<T>, std::forward_list<T>>>
    insertionCppStl (C<T> & container) {
    // do seomthing here for list
}

(caution: code not tested)

As pointed by Daniel Langr (thanks!) this works only for lists with standard allocator, so for std::list<T> and std::forward_list<T> , not for std::list<T, A> and std::forward_list<T, A> where A is an allocator different from std::allocator<T> .

This can be enough for you, or not, depending from your needs.

A more generic solution can take in count also the allocator

template <template <typename...> class C, typename T, typename A>
std::enable_if_t<std::is_same_v<C<T, A>, std::list<T, A>>
              || std::is_same_v<C<T, A>, std::forward_list<T, A>>>
    insertionCppStl (C<T, A> & container) {
    // do seomthing here for list
}

Obviously this works only for std::list and std::forward_list .

As Barry pointed in a comment (thanks!), a similar solution can be implemented when you have two (three, four...) types with something in common (in this case: template types with compatible signatures) but isn't a general solution.

Unfortunately I don't see a trivial general solution... I propose the development of a specific type_traits to select the accepted types for a specific version of the function.

For example: if you want a specific version for std::list s, std::forward_list s and std::array s, you can write something as follows

template <typename>
struct specific_foo : public std::false_type
 { };

template <typename T, typename A>
struct specific_foo<std::list<T, A>> : public std::true_type
 { };

template <typename T, typename A>
struct specific_foo<std::forward_list<T, A>> : public std::true_type
 { };

template <typename T, std::size_t N>
struct specific_foo<std::array<T, N>> : public std::true_type
 { };

Then two version of the foo() function: the specific version (receiving a std::true_type as second argument) and a generic one (receiving a std::false_type

template <typename T>
void foo (T const &, std::false_type)
 { std::cout << "generic version" << std::endl; }

template <typename T>
void foo (T const &, std::true_type)
 { std::cout << "specific version" << std::endl; }

Now you need a tag-dispatching version of foo() that select the correct type for the second argument

template <typename T>
void foo (T const & t)
 { foo(t, specific_foo<T>{}); }

The following is a full compiling example

#include <list>
#include <array>
#include <vector>
#include <iostream>
#include <type_traits>
#include <forward_list>

template <typename>
struct specific_foo : public std::false_type
 { };

template <typename T, typename A>
struct specific_foo<std::list<T, A>> : public std::true_type
 { };

template <typename T, typename A>
struct specific_foo<std::forward_list<T, A>> : public std::true_type
 { };

template <typename T, std::size_t N>
struct specific_foo<std::array<T, N>> : public std::true_type
 { };

template <typename T>
void foo (T const &, std::false_type)
 { std::cout << "generic version" << std::endl; }

template <typename T>
void foo (T const &, std::true_type)
 { std::cout << "specific version" << std::endl; }

template <typename T>
void foo (T const & t)
 { foo(t, specific_foo<T>{}); }

int main ()
 {
   foo(0);                             // print "generic version"
   foo(std::list<int>{});              // print "specific version"
   foo(std::forward_list<long>{});     // print "specific version"
   foo(std::array<long long, 42u>{});  // print "specific version"
   foo(std::vector<char>{});           // print "generic version"
 }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM