如何在c++中迭代变量模板参数?

uz75evzq  于 2022-12-05  发布在  其他
关注(0)|答案(3)|浏览(208)

我正在尝试使用变量模板(这是第一次)来创建一个字符串替换函数。
实际上,我想创建一个函数(我们称之为Replace),该函数接受一个键值,用于根据Replace函数中提供的附加参数搜索和修改模板字符串。模板字符串包含使用字符串“%s”的占位符。
我的问题是,我不确定如何迭代变量模板参数......下面是一些示例代码。

const std::string FindOriginal(std::string Key)
{
    if(Key == "Toon")
    {
        return "This is example %s in my program for Mr. %s.";
    }
    
    return "";
}

std::string Replace(std::string OriginalKey) {
    return "";
}

template<typename First, typename ... Strings>
std::string Replace(std::string OriginalKey, First arg, const Strings&... rest)
{
    const std::string from = "%s";
    std::string the_string = FindOriginal(OriginalKey);
    
    int i = 0;
    size_t start_pos = the_string.find(from);
    while(start_pos != std::string::npos)
    {
        // Ideally here I can somehow get the parameter at index i... 
        the_string.replace(start_pos, from.length(), rest[i]);
        
        start_pos = the_string.find(from, start_pos);
        i++;
    }
    
    Replace(rest...);
    
    return the_string;
}

int main()
{
    std::cout << Replace("Toon", "5", "Jimmy") << std::endl;    
}
js4nwp54

js4nwp541#

如果你能访问C++17或更高版本,你的逻辑最好用fold expression来表达,它可以为包中的每个参数对你的字符串应用一个函数(一元右折叠):

template<class T>
const std::string& ReplaceOne(std::string& originalString, const T& replacement)
{
    static constexpr auto sentinel = "%s";
    size_t start_pos = originalString.find(sentinel);
    if(start_pos != std::string::npos)
        originalString.replace(start_pos, 2, replacement);
    return originalString;
}

template<class...Ts>
void Replace(std::string& originalString, const Ts&... rest)
{
    (ReplaceOne(originalString, rest) , ...);
}

卿卿如是说:

std::string original = "This is example %s in my program for Mr. %s.";
    const std::string expected = "This is example 5 in my program for Mr. Jimmy.";
Replace(original, "5", "Jimmy");
assert(original == expected);

Live Demo(第一个字母)

本质上,我们对字符串调用ReplaceOne,用下一个变量参数替换"%s"的一个示例,如果没有找到"%s",我们就返回不变的字符串。
我们使用一元右折叠,以便从左到右处理参数,因为顺序很重要。
这种方法(实际上就是你的答案中的方法)的缺点是,你为每个变量参数调用std::string::find,这可能会搜索整个字符串。在最坏的情况下,字符串中甚至没有"%s"的示例,结果,你为每个替换都搜索整个字符串。这是低效的; O(N * M),其中N是字符串的长度,M是参数包中的参数数。
如果我们从find的最后一个结果开始重复调用find,我们可以将时间复杂度降低到O(N)
(另一种方法是将替换字符串存储在临时容器中,然后在this answer(现已删除)中建议的Stack Danny循环中迭代该字符串。

aiazj4mn

aiazj4mn2#

您可以:

  • 搜寻要取代的样式。
  • 使用输入字符串后缀和其余参数(即rest)进行递归调用。
  • 然后连接输入字符串前缀、当前的arg以及递归调用返回的任何内容。
  • 把它还回去。

我已经将所有这些逻辑放入另一个函数ReplaceImpl中,并让Replace执行早期检查(例如,找到原始密钥,如果没有找到原始密钥,则返回)。
Demo(https://godbolt.org/z/7zfGjx1P9)

##include <iostream>
#include <string>

const std::string FindOriginal(std::string Key) {
    if (Key == "Toon") {
        return "This is example %s in my program for Mr. %s.";
    }
    return {};
}

std::string ReplaceImpl(std::string str) {
    return str;
}

template <typename First, typename... Strings>
std::string ReplaceImpl(std::string str, First arg, const Strings&... rest) {
    if (auto pos{ str.find("%s") }; pos != std::string::npos) {
        return
            str.substr(0, pos) + 
            arg +
            ReplaceImpl(str.substr(pos + 2), rest...);
    }
    return str;
}

template <typename First, typename... Strings>
std::string Replace(std::string OriginalKey, First arg, const Strings&... rest) {
    if (auto the_string{ FindOriginal(OriginalKey) }; not the_string.empty()) {
        return ReplaceImpl(the_string, arg, rest...);
    }
    return {};
}

int main() {
    std::cout << Replace("Toon", "5", "Jimmy") << "\n";
}

// Outputs: This is example 5 in my program for Mr. Jimmy.
u0sqgete

u0sqgete3#

多亏了某人对通过递归调用工作的可变模板的评论,我设法修改了解决方案以获得期望的结果。基本上,我们将参数提供给一个模板化函数(GetModifiedString),该函数调用可变模板函数(ReplaceValue)。

#include <iostream>
#include <string>

const std::string FindOriginal(const std::string& Key)
{
    if(Key == "Toon")
    {
        return "This is example %s in my program for Mr. %s.";
    }
    
    return "";
}

void ReplaceValue(std::string& OriginalString)
{
    (void)OriginalString;
}

template<typename First, typename ... Strings>
void ReplaceValue(std::string& OriginalString, First arg, const Strings&... rest)
{
    const std::string from = "%s";

    size_t start_pos = OriginalString.find(from);
    if(start_pos != std::string::npos)
    {
        OriginalString.replace(start_pos, from.length(), arg);
    }
    
    ReplaceValue(OriginalString, rest...);
}

template<typename First, typename ... Strings>
std::string GetModifiedString(const std::string& OriginalKey, First arg, const Strings&... rest)
{
    std::string modified_string = FindOriginal(OriginalKey);
    ReplaceValue(modified_string, arg, rest...);
   
    return modified_string;
}

int main()
{
    std::cout << GetModifiedString("Toon", "5", "Jimmyy") << std::endl;    
}

相关问题