【问题标题】:Failure in template type resolution模板类型解析失败
【发布时间】:2022-01-10 17:04:11
【问题描述】:

这让我发疯了。我只想将字符串对的可变参数列表发送到替换字符串中单词的模板函数中,并且该函数可以工作,但只有一个参数,而不是可变参数。

这很好用

using std::string;
using spair = std::pair<string, string>;


string replace_all(string& str, const spair& sp)
 {
    string::size_type pos = 0;
    string::size_type rmsize = std::get<0>(sp).size();

  while((pos = str.find(std::get<0>(sp), pos)) != string::npos)
     {
        str.replace(pos, rmsize, std::get<1>(sp));
        pos++;
     }

  return str;

}

string pattern = "My name is {{first_name}} {{last_name}} and I live in {{location}}";

string t = replace_all( pattern,  {"{{first_name}}","Homer"}  );

/*************************/

但这行不通

template<typename T=spair, typename  ...Args>
string replace_all(string& s, const T& arg1,  Args const&... args )
{
   s = replace_all(s, arg1);
  
   s = replace_all(s, args...);

  return s;  
}

string v = replace_all( pattern,         
          {"{{first_name}}","Homer"},
          {"{{last_name}}","Simpson"}, 
          {"{{location}}","Springfield"} 
          );

variadic.cpp:72:8: error: no matching function for call to **'replace_all(std::string&, , , )'

【问题讨论】:

  • 您遇到了可变参数的根本弱点。带括号的初始化列表是无类型的,它们不能绑定到需要绑定离散类型的模板参数。这是模板如何工作的基本方面,这里没有干净的解决方案。您要么必须显式键入每个参数,而不是提供原始的大括号初始化列表,要么更改整个方法。

标签: c++ templates parameters variadic


【解决方案1】:

这里可能存在对初始化列表和模板的滥用。总结一下:

  • 内部类型是已知的,它总是一对字符串。
  • “替换对”的数量应该是可配置的

在此处使用模板,将强制在 replace_all 以不同数量的对调用时生成新的(函数)类型。如果您像这样依赖“运行时列表”,您可以大大简化:

string replace_all(string& s, std::vector<spair> const& v)
{
    for (auto const& sp : v) replace_all(s, sp);
    return s;  
}

// call it like so
replace_all(pattern, 
  {         
    {"{{first_name}}","Homer"},
    {"{{last_name}}","Simpson"}, 
    {"{{location}}","Springfield"} 
  }
) << std::endl;

Demo

正如@Sam Varshavchik 在 cmets 中提到的那样,使用您建议的方法进行模式匹配有点问题。如果你像这样调用你的函数,它可能会起作用:

replace_all(pattern,        
    spair{"{{first_name}}","Homer"},
    spair{"{{last_name}}","Simpson"}, 
    spair{"{{location}}","Springfield"} 
);

从而使推导的类型显式。

【讨论】:

  • 更轻的重量应该是:string replace_all(string& s, std::initializer_list const& il) 我试过了,它可以工作。
  • @ChrisReid even even lighter 将使用数组 template &lt;size_t N&gt; string replace_all(string &amp;s, spair const (&amp;v)[N]) 但我怀疑您实际上会从这些更改中获得任何性能。我会优化可读性,所有这些变化基本上都是一样的(只有当你想避免额外的“包含”时,数组解决方案才有优势)
【解决方案2】:

我无法完全理解,但我设计了一个解决方法:

// 1 to 5 replacement pairs
const spair emptyspair{{},{}};  // A "nothing" pair of empty strings

string replace_all(string& s,
           const spair& sp1=emptyspair,
           const spair& sp2=emptyspair,
           const spair& sp3=emptyspair,
           const spair& sp4=emptyspair,
           const spair& sp5=emptyspair
           )
{
  
  std::array<spair,5> v{sp1, sp2, sp3, sp4, sp5};
  
  for (spair sp : v )
    {
      if (sp != emptyspair)
         {
            auto& s1 = std::get<0>(sp); 
            auto& s2 = std::get<1>(sp);
            s = replace_all(s, s1, s2);
         }
    }
  
  return s;
}

这允许 1 到 5 个 spair 参数,默认为 dummy pair 每个在包含它们的数组中都被忽略。 我认为所有引用都绑定到同一个默认对象 所以不要创建 10 个字符串对象和 5 个对, 并且不等式检查会很快与同一个对象进行比较。

【讨论】:

    猜你喜欢
    • 2018-03-10
    • 2018-02-19
    • 2012-10-30
    • 1970-01-01
    • 1970-01-01
    • 2019-05-23
    • 2015-12-16
    • 1970-01-01
    相关资源
    最近更新 更多