kirillbobyrev
kirillbobyrev

Reputation: 1729

Template specialisation issue

I'd like to call template<typename T> foo(T x) and manually handle these cases: T = std::vector<U>, T = std::string, T = any other case.

Here's what I wrote for that:

#include <iostream>
#include <vector>
#include <string>

template<typename T> void foo_impl(const std::string &data, std::string *) {
  std::cout << "foo for std::string called\n";
}

template<typename T> void foo_impl(const T &data, T *) {
  std::cout << "foo for general types called\n";
}

template<typename T> void foo_impl(const std::vector<T> &data, std::vector<T> *) {
  std::cout << "foo for std::vector<T> called\n";
}

template<typename T> void foo(const T &data) {
  foo_impl(data, static_cast<T*>(nullptr));
}


int main() {
  int i = 1;
  foo(i);
  std::vector<int> a = {0, 1};
  foo(a);
  std::string s = "abcd";
  foo<std::string>(s);
  return 0;
}

However, foo(std::string x) is called as in case "T is any other type". How do I deal with it?

Upvotes: 0

Views: 78

Answers (3)

Jarod42
Jarod42

Reputation: 217145

For template:

template<typename T> void foo(const T &data) {
  std::cout << "foo for general types called\n";
}

Following is a specialization:

template<> void foo<>(const std::string &data) {
    std::cout << "foo for std::string called\n";
}

but simple overload seems more appropriate:

void foo(const std::string &data) {
    std::cout << "foo for std::string called\n";
}

As partial specialization is not possible for function, you have to make a overload for vector case:

template<typename T, typename Alloc> void foo(const std::vector<T, Alloc> &data) {
  std::cout << "foo for std::vector<T, Alloc> called\n";
}

An alternative is to forward to a class/struct which can be (partially) specialized:

template <typename T>
struct foo_impl {
    void operator (const T&) const
    {
        std::cout << "foo for general types called\n";
    }
};

// specialization for std::string
template <>
struct foo_impl<std::string>
{
    void operator (const T&) const
    {
        std::cout << "foo for std::string called\n";
    }
};

// partial specialization for std::vector
template <typename T, typename A>
struct foo_impl<std::vector<T, A>>
{
    void operator (const std::vector<T, A>&) const
    {
        std::cout << "foo for std::vector<T, A> called\n";
    }
};

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

Upvotes: 4

R Sahu
R Sahu

Reputation: 206567

It's not clear to me why you are using two layers of functions..

You can overload foo for std::string and std::vector<T>.

#include <iostream>
#include <vector>
#include <string>

template<typename T> void foo(const T &data) {
  std::cout << "foo for general types called\n";
}

template <typename T> void foo(const std::vector<T> &data) {
  std::cout << "foo for std::vector<T> called\n";
}

void foo(const std::string &data) {
  std::cout << "foo for std::string called\n";
}

int main() {
  int i = 1;
  foo(i);
  std::vector<int> a = {0, 1};
  foo(a);
  std::string s = "abcd";
  foo(s);
  return 0;
}

Output:

foo for general types called
foo for std::vector<T> called
foo for std::string called

Upvotes: 2

Piotr Skotnicki
Piotr Skotnicki

Reputation: 48447

T from template<typename T> void foo_impl(const std::string &data, std::string *) is non-deducible (that is, it's not used in the parameter list of the function), as such, it's not considered as a viable overload.

You can remove the template<typename T> part and make this overload a non-template:

void foo_impl(const std::string &data, std::string *) {
  std::cout << "foo for std::string called\n";
}

Upvotes: 3

Related Questions