【问题标题】:Using templates and strings?使用模板和字符串?
【发布时间】:2016-03-15 07:01:20
【问题描述】:

我有一个方便的函数,可以将字符串拆分为多个部分。实施对我的问题并不重要。

inline std::vector<std::string> & split(const std::string & strInput, const std::string & strPattern, std::vector<std::string> & vec_strPart)
    {...}

我想制作这个函数的模板版本,以支持 wstrings 和其他类型的字符串。但是,如果我这样做

template <class StringType>
inline std::vector<StringType> & tsplit(const StringType & strInput, const StringType & strPattern, std::vector<StringType> & vec_strPart)
    {...}

那么它就不能按预期工作

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

我的问题是为什么使用字符串常量不适用于模板版本,而它适用于未模板版本。我的假设是,在未模板化的情况下,存在从 char[]std::string 的隐式转换,而对于模板,匹配发生在隐式转换之前。

如何解决“歧义问题”?我可以制作一个专门的tsplit 版本来转换为std::string 并调用tsplit吗?

【问题讨论】:

标签: c++ string templates stl


【解决方案1】:

std::stringconst char* 是不同的类型,所以在

tsplit(str, " ", vec_strPart);

如果您希望 StringTypestd::stringconst char*,这是不明确的。

解决这个问题的一种方法是为每个参数设置模板:

template <typename String, typename InputString, typename PatternString>
std::vector<String>& tsplit(const InputString & input,
                            const PatternString& pattern,
                            std::vector<String>& res);

另一种方法是仅对一个参数进行推导,并使某些参数不可推导:

// 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);

【讨论】:

  • 这是 C++03 还是 C++11?
  • 可以在c++03中完成,将using type = t替换为typedef t type;,删除non_deducible_t的定义,将non_deducible_t&lt;String&gt;替换为typename non_deducible&lt;String&gt;::type
  • 不可演绎的好技巧。还 +1 实际回答 OP 的问题。
【解决方案2】:

如果你这样声明你的函数

template <class StringType, class TextTypeA, class TextTypeB>
inline std::vector<StringType> & tsplit(TextTypeA strInput, TextTypeB strPattern, std::vector<StringType> & vec_strPart)
{
 ...   
}

然后您的示例编译。然后将自动转换推迟到您的实施。

所以实施很重要;-)

【讨论】:

  • 我是否需要在实施开始时显式转换?像这样const StringType strInputInternal(strInput); const StringType strPatternInternal(strPattern); 另一个问题:使用call-by-value而不是call-by-ref,你有什么特殊原因吗?
  • 显式转换可能是一个简单的解决方案,但也可以使用其他选项。转换可以自动启动。例如,如果您使用 find_if 明确地使用 StringType 来避免歧义。
  • 关于按值调用,const ref 可能更好,你是对的。但必须经过测试。
【解决方案3】:
template <
    typename StringType,
    typename PatternStringType,
    typename ResultsStringType>

inline std::vector<ResultsStringType>& tsplit(
    const StringType& strInput,
    const PatternStringType& strPattern,
    std::vector<ResultsStringType>& vec_strPart)
{
    return vec_strPart; // your implementation
}

题外话:如果你给第三个参数为什么要返回值,你真的需要模板参数来生成字符串类型吗?也许最好有以下之一:

template <typename StringType, typename PatternStringType>
inline std::vector<StringType> tsplit(const StringType& strInput, const PatternStringType& strPattern)
{
    return std::vector<StringType>(); // your implementation
}

template <typename StringType, typename PatternStringType>
inline void tsplit(const StringType& strInput, const PatternStringType& strPattern, std::vector<StringType>& resultingStrs)
{
    // your implementation
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-28
    • 2011-05-14
    • 2016-06-08
    • 2018-07-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多