简体   繁体   中英

Default parameter value in template function, depending on type

Suppose I have this function to trim an std::string and decide to extend it, so that it removes not only spaces from the beginning and end, but also any character I pass in another string, like spaces, newlines and carriage returns.

std::string Trim ( const std::string &In_Original,
  const std::string &In_CharsToTrim = " \n\r" );

So basically, it would remove all characters present in In_CharsToTrim from the beginning and end of In_Original.

Now, I had to write this for both std::string and std::wstring. Since I find that absurd, I chose to make use of templates, and it works, but I am unable to pass a default value, because the std::wstring version must get L" \\n\\r" as the default value (note that little L, there).

I tried this:

template <typename Type> Type Trim ( const Type &In_String,
  const Type &In_TrimCharacters );

along with:

template std::string Trim ( const  std::string &In_String,
  const  std::string &In_TrimCharacters = " \n\r" );
template std::wstring Trim ( const  std::wstring &In_String,
  const  std::wstring &In_TrimCharacters = L" \n\r" );

But it didn't work. It doesn't even compile.

Currently, what I have, is a separate function for when you call without a second parameter, but this is completely the wrong approach:

template <typename Type> Type Trim ( const Type &In_String,
  const Type &In_TrimCharacters );
std::string Trim ( const std::string &In_String );
std::wstring Trim ( const std::wstring &In_String );

Then, in this simpler Trim function, I'm simply calling the complete version.

So, basically, what I'm asking is this...:

How can I pass a different default value depending on the template type? Basically, a specialization, in which the only change is the default parameter...

In this case, to pass std::string ( " \\n\\r" ) in one case, and std::wstring ( L" \\n\\r" ) in the other...

Or... is there another way to do what I'm trying to do, here?

You can use a simple trait class and using its ::value as the default argument.

template<typename T>
struct type_sensitive_default_argument;

template<>
struct type_sensitive_default_argument<std::string>
{
     static constexpr char value[] = "\n\r";
};

template<>
struct type_sensitive_default_argument<std::wstring>
{
    static constexpr wchar_t value[] = L"\n\r";
};

Then you can do:

template<class Type,
         class Default = type_sensitive_default_argument<Type>>
Type Trim(const Type& In_String,
          const Type& In_TrimCharacters = Default::value);

This is what I come up with:

template <typename T> struct default_trim_chars;

template<> struct default_trim_chars<std::string> { 
    static const char* value() { return " \n\r"; }
};

template<> struct default_trim_chars<std::wstring> { 
    static const wchar_t* value() { return L" \n\r"; }
};

template <typename Type> Type Trim ( const Type &In_String,
const Type &In_TrimCharacters = default_trim_chars<Type>::value()){
    /* ... */
}

You can also declare value as a data member instead of a function, but that would require constexpr and C++11.

In your case you might rely on specifying an initializer list of characters to do the conversion for you:

template <typename Type> Type Trim ( const Type &In_String,
  const Type &In_TrimCharacters = { ' ', '\r', '\n' } )
{
    // ...
}

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