簡體   English   中英

如何迭代 c++ 中的可變參數模板參數?

[英]How can I iterate over variadic template parameters in c++?

我正在嘗試使用可變參數模板(真的是第一次)來替換字符串 function。

本質上,我想制作一個 function(我們稱之為替換),它采用一個鍵值,該鍵值用於根據替換 function 中提供的附加參數搜索和修改模板字符串。模板字符串具有使用字符串“%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;    
}

如果您有權訪問 C++17 或更高版本,您的邏輯最好用折疊表達式表示,它可以將 function 應用於包中每個參數的字符串(一元右折疊):

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

現場演示

本質上,我們正在對我們的字符串調用ReplaceOne以將"%s"的單個實例替換為下一個可變參數。 如果沒有找到"%s" ,我們只返回不變的字符串。

我們使用一元右折疊,以便我們從左到右處理參數,因為順序很重要。

這種方法(實際上是您答案中的方法)的缺點是您正在為每個可變參數調用std::string::find ,這可能會搜索整個字符串。 在最壞的情況下,甚至在您的字符串中都沒有"%s"的實例,因此,您在整個字符串中搜索每個替換項。 這是低效的; O(N * M)其中N是字符串的長度, M是參數包中 arguments 的數量。

如果我們從find的最后一個結果開始重復調用find ,我們可以將時間復雜度降低到O(N) 我現在將其作為練習留給您。

(另一種方法是將替換字符串存儲在一個臨時容器中,並像Stack Danny這個答案中建議的那樣循環遍歷字符串(現已刪除)

你可以:

  • 搜索要替換的模式。
  • 使用輸入字符串后綴和 arguments 的 rest(即rest )進行遞歸調用。
  • 然后連接輸入字符串前綴、當前arg以及遞歸調用返回的任何內容。
  • 並返回那個。

我已將所有這些邏輯放入另一個 function ReplaceImpl中,並讓Replace進行早期檢查(例如,找到原始密鑰並在未找到原始密鑰時返回)。

[演示]

##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.

感謝某人關於通過遞歸調用工作的可變參數模板的評論,我設法更改了解決方案以獲得所需的結果。 基本上,我們將參數提供給調用可變參數模板 function (ReplaceValue) 的模板化 function (GetModifiedString)。

#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;    
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM