1

I have a class with template function and a special function, which looks like:

#include <bits/stdc++.h>
using namespace std;

class A
{ 
 public:
  template <typename T>
  void test(const T& t) 
  {
    cout << "template " << t << endl;
  }

  void test(const std::string& s) {
    cout << "test " << s << endl;
  }
};

int main() 
{
  A a;
  a.test("asdas");
}

As you could see, there are two test function:

  1. template function
  2. special function which only match std::string input.

What I want is:

  1. test(1) -> call template function test<int>(1)
  2. std::string str = "asd"; test(str); -> call special function test(str)
  3. test("asd") -> call special function test(std::string str = "asd")

How to achieve this?

JeJo
  • 30,635
  • 6
  • 49
  • 88
nick
  • 832
  • 3
  • 12
  • See [Why should I not #include ?](https://stackoverflow.com/q/31816095) and [Why using namespace std is bad practice](https://stackoverflow.com/questions/1452721). – prapin Aug 03 '22 at 18:21

1 Answers1

5

The asdas is not a std::string, rather a string literal which can referred as const char*. That is why a.test("asd") was not calling the member void test(std::string const& s).


How to achieve this?

One way to fix is proving one more member overload

class A 
{
public:
    // ... code
    void test(std::string const& s) {
        cout << "test " << s << endl;
    }

    void test(const char* s) {   // overload for string literals
        test(std::string{s});
    }
};

See a demo

Alternatively, in you can SFINAE the two functions

class A
{
public:
    template <typename T>
    auto test(const T& t) // T can not be convertible to std::string
        -> typename std::enable_if<!std::is_convertible<T, std::string>::value>::type
    {
        std::cout << "template " << t << std::endl;
    }

    template<typename T>
    auto test(T const& s) // T can be convertible to std::string
        -> typename std::enable_if<std::is_convertible<T, std::string>::value>::type
    {
        std::cout << "test " << s << std::endl;
    }
};

See a demo


However, if you are able to at-least upgrade to , you could use a string literal, and pass the std::string to the void test(std::string const& s) (i.e. no more member overloads required)

using namespace std::string_literals;

a.test("asd"s);
//         ^^^

See a demo


One step up, in we can decide what part of condition to be compiled/ discarded at compile time using if constexpr.

class A 
{
public:
    template <typename T>
    void test(const T& t)
    {
        if constexpr (!std::is_convertible_v<T, std::string>)
        {
            std::cout << "template " << t << std::endl;
        }
        else
        {
            std::cout << "test " << t << std::endl;
        }
    }
};

See a demo


Also have a read:

JeJo
  • 30,635
  • 6
  • 49
  • 88
  • 2
    SFINAE can even be done before C++11. It is just the provided tools/traits (such as `std::enable_if`) which have to be reimplemented. – Jarod42 Jul 12 '22 at 11:13