I have a handy function which splits a string into parts. The implementation is not important for my question.
inline std::vector<std::string> & split(const std::string & strInput, const std::string & strPattern, std::vector<std::string> & vec_strPart)
{...}
I want to make a templated version of this function in order to support wstrings and other kinds of strings. However, if I do this
template <class StringType>
inline std::vector<StringType> & tsplit(const StringType & strInput, const StringType & strPattern, std::vector<StringType> & vec_strPart)
{...}
then it does not work as expected
const std::string str("bla bla blaaaa");
const std::string strPattern(" ");
std::vector<std::string> vec_strPart;
split(str, strPattern, vec_strPart); // works
tsplit(str, strPattern, vec_strPart); // works
split(str, " ", vec_strPart); // works
tsplit(str, " ", vec_strPart); // does not work, StringType is ambiguous
tsplit(str, std::string(" "), vec_strPart); // works but changes client's code
My question is why using string constants does not work with the templated version while it works with the untemplated one. My assumption is that in the untemplated case, there is an implicit conversion from char[] to std::string while for templates, the matching occurs before implicit conversion.
How can I remedy the "ambiguity problem"? Can I maybe make a specialized version of tsplit which does the conversion to std::string and calls tsplit?
std::string and const char* are different type, so in
tsplit(str, " ", vec_strPart);
It is ambiguous if you want that StringType is std::string or const char*.
One way to fix that is to have template for each argument:
template <typename String, typename InputString, typename PatternString>
std::vector<String>& tsplit(const InputString & input,
const PatternString& pattern,
std::vector<String>& res);
An other way is to deduce only for one parameter, and make some arguments non deducible:
// Helper
template <typename T> struct non_deducible { using type = t; };
template <typename T> using non_deducible_t = typename non_deducible<T>::type;
template <typename String>
std::vector<String>& tsplit(const non_deducible_t<String>& input,
const non_deducible_t<String>& pattern,
std::vector<String>& res);
If you declare your function like this
template <class StringType, class TextTypeA, class TextTypeB>
inline std::vector<StringType> & tsplit(TextTypeA strInput, TextTypeB strPattern, std::vector<StringType> & vec_strPart)
{
...
}
Then your example compiles. The automatic conversion is then deferred to your implementation.
And so implementation matters ;-)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With