0

I tried to declare a function that converts const char * to std::basic_string in the following way:

#include <string>
#include <cstring>

#ifdef _MSC_VER
#include <tchar.h>
#else
#define TCHAR char
#endif

typedef TCHAR Char;

typedef std::basic_string<Char> String;

template <typename = typename std::enable_if< !std::is_same<Char, char>::value >::type >
inline String FromACString(const char * p_src)
{
    String dest(std::strlen(p_src), ' ');

    auto p_cur = p_src;

    for (auto & ch : dest)
    {
        ch = *p_cur++;
    }

    return dest;
}

template <typename = typename std::enable_if< std::is_same<Char, char>::value >::type >
inline String FromACString(const char * p_src)
{
    return p_src;
}

but got compiler errors. My idea was to have two overloads and enable one of them depending on TCHAR type. What is the proper way to do this?

The error with VC2017 is:

error C2995: 'String FromACString(const char *)': function template has already been defined
melpomene
  • 84,125
  • 8
  • 85
  • 148
Alexey Starinsky
  • 3,699
  • 3
  • 21
  • 57

2 Answers2

3

Miles' answered what's wrong with your enable_if. However, you can avoid the nuances of enable_if by using if constexpr:

inline std::basic_string<TCHAR>
FromACString(const char * p_src)
{
    if constexpr (std::is_same_v<TCHAR, char>) {
        // logic for case where TCHAR is char
    } else {
        // logic for case where TCHAR isn't char
    }
}

PS. Your TCHAR conversion to wchar_t (which I assume is the only other possible type besides char) should probably take character encoding into consideration. You might use mbsrtowcs.

eerorika
  • 232,697
  • 12
  • 197
  • 326
2

Your code has two issues:

  1. SFINAE only works in dependent contexts. Since nothing in typename std::enable_if<!std::is_same<Char, char>::value>::type is dependent on any template parameters, SFINAE does not apply, and you get a hard error since std::enable_if<false>::type is invalid.
  2. When determining the "uniqueness" of templates, only the parameter types themselves are considered, not their default values. You have two definitions of the function template template <typename> String FromACString(const char*), and that is an error.

To fix the first problem, you can introduce another template parameter with a default value of Char. The usual fix to the second problem is to use the enable_if expression itself as the template parameter type, instead of a default. That leaves you with something like this:

template <typename C = Char,
          std::enable_if_t<!std::is_same<C, char>::value>* = nullptr>
String FromACString(const char* p_src) {
    //...
}

template <typename C = Char,
          std::enable_if_t<std::is_same<C, char>::value>* = nullptr>
String FromACString(const char* p_src) {
    //...
}

Live Demo

Now, depending on how Char is defined, you end up with one template with the signature

template <typename, void*>
String FromACString(const char*);

and another that is ill-formed and therefore disabled by SFINAE.

Miles Budnek
  • 28,216
  • 2
  • 35
  • 52