C++ 模板参数推导/替换失败:

Posted

技术标签:

【中文标题】C++ 模板参数推导/替换失败:【英文标题】:C++ template argument deduction/substitution failed: 【发布时间】:2018-04-28 23:47:19 【问题描述】:

我对 C++ 很陌生,我正在体验它的一些功能。特别是,我想实现一个通用的分而治之过程,它以“功能代码”为参数来解决问题。实际上我想到的是“分而治之”是一个以参数为参数的高阶函数:

一个函数basecase() 来测试我们是否达到了基本情况 一个函数 solvecase() 解决基本情况 一个函数divide()将问题分成两个子问题 一个函数conquer() 采用部分解决方案并从两个解决方案中构建一个解决方案。

为此,我使用了模板:特别是,A 是输入问题的类型,B 是输出问题的类型,C 将可能是布尔值(基本案例测试的结果类型)。

还有其他东西,比如使用async() 来同时计算解决方案,但这不是我的问题的重点。

我的问题是:为什么我在编译时会收到以下消息,抱怨冲突类型

D&C.cpp: In function ‘int main(int, char**)’:
D&C.cpp:67:92: error: no matching function for call to ‘divideAndConquer(std::string&, main(int, char**)::__lambda4&, main(int, char**)::__lambda5&, main(int, char**)::__lambda6&, main(int, char**)::__lambda7&)’
     divideAndConquer(text, lambda_basecase, lambda_divide, lambda_conquer, lambda_solvecase);
                                                                                            ^
D&C.cpp:67:92: note: candidate is:
D&C.cpp:10:3: note: template<class A, class B, class C> B divideAndConquer(A, C, A, B, B)
 B divideAndConquer(const A x, C basecase, A divide, B conquer, B solvecase)
   ^
D&C.cpp:10:3: note:   template argument deduction/substitution failed:
D&C.cpp:67:92: note:   deduced conflicting types for parameter ‘A’ (‘std::basic_string<char>’ and ‘main(int, char**)::__lambda5’)
     divideAndConquer(text, lambda_basecase, lambda_divide, lambda_conquer, lambda_solvecase);

代码如下:基本上这个想法是使用分而治之的过程来寻找目标字符串的实例到文本中,但要足够通用以重用此模式来解决其他问题(例如斐波那契数)。

#include <iostream>
#include <thread>
#include <string>
#include <fstream>
#include <future>

using namespace std;

template <typename A, typename B, typename C>
B divideAndConquer(const A x, C basecase, A divide, B conquer, B solvecase)

    if (basecase(x))
    return solvecase(x);

    A probl1 = divide(x,1);
    A probl2 = divide(x, 2);

    auto sol1f = async(divideAndConquer, probl1, basecase, divide, conquer, solvecase);
    B sol2 = divideAndConquer(probl2, basecase, divide, conquer, solvecase);
    B sol1 = sol1f.get();

    return (conquer(sol1, sol2));


int splitOnWhitespaces(const string& text, int half, string direction)
    int i=half, N = text.length();
    if (direction.compare("forward")==0)    
        while(text[i]!=' ' && text[i] != '\n' && i < N)
            i++;
    
    else
         while(text[i]!=' ' && text[i] != '\n' && i >= 0)
            i--;
    if (i<=0 || i==N)
        return -1;
    else
        return i;
    


int main(int argc, char *argv[])
    string text= "Nel mezzo del cammin di nostra vita mi ritrovai per una selva oscura, che la diritta via era smarrita.";
    string target = "nel";
    int base = 10;
    auto lambda_basecase = [base](string text)return text.length()<=base;;
    auto lambda_divide= [](string text, int subprobl)
         int half = text.length()/2;
         half = splitOnWhitespaces(text, half, "forward");
         if (half==-1)
            half = splitOnWhitespaces(text, half, "backward");
         if (half==-1)
             throw runtime_error("Error splitting the string: no whitespaces\n");
         return ((subprobl==1) ? string(text, 0, half) : string(text, half));
     ;
    auto lambda_conquer = [](int count1, int count2)return count1+count2;;    
    auto lambda_solvecase = [target](string baseText)
        int found = 0;
        int start = 0;
        int len = target.length();
        while(baseText.find(target, start)!=string::npos )
            found++;
            int position = baseText.find(target, start);
            start = position + len + 1;
        
        return found;
    ;
    divideAndConquer(text, lambda_basecase, lambda_divide, lambda_conquer, lambda_solvecase);
    return 0;

我现在正在学习模板编程,因此,对于这个问题的任何帮助以及对解释此事的(好)书籍或网页的任何参考都将不胜感激。 提前谢谢!

【问题讨论】:

您的意思是写const std::string x 而不是const A x?否则参数的类型不匹配(如果是,我们应该作为错字关闭)。 btw const std::string&amp; x 可能是更好的选择。 这里有很多印刷错误,所以我投票结束。您似乎将您的模板类型与其他类型混合在一起。同时,我想在不发布答案的情况下帮助您,所以我模拟了一些可以编译 here 的东西。请注意,您也可能有逻辑错误。 您希望 divideAndConquer 函数的第一个和第三个参数共享类型(const 除外),但传递 std::string 和 lambda。它们具有不同的类型,导致缺乏匹配。 【参考方案1】:

首先,一个建议:为模板类型命名比ABC、...更有用(建议使用)

因此,您为完全不同的事物提供了相同的类型标识符(A 用于 std::string 和 lambda 函数;B 用于两种不同的 lambda 类型(并考虑到不同的 lambda 是不同的类型;也有相同的签名)和返回值(int,在您的示例中)。

我建议以下版本

template <typename dataT, typename divT, typename conqT, typename baseT,
          typename solvT>
auto divideAndConquer (dataT const & x, baseT basecase, divT divide,
                       conqT conquer, solvT solvecase)
   -> decltype(solvecase(x))
 
   using retT = decltype(solvecase(x));

   if ( basecase(x) )
      return solvecase(x);

   dataT const probl1  divide(x, 1) ;
   dataT const probl2  divide(x, 2) ;

   auto sol1f = async<retT(*)(dataT const &, baseT, divT, conqT, solvT)>
      (divideAndConquer, probl1, basecase, divide, conquer, solvecase);

   retT sol2  divideAndConquer(probl2, basecase, divide, conquer,
                                solvecase) ;
   retT sol1  sol1f.get() ;

   return conquer(sol1, sol2);
 

【讨论】:

以上是关于C++ 模板参数推导/替换失败:的主要内容,如果未能解决你的问题,请参考以下文章

C++ 编译报错:couldn’t deduce template parameter ‘xxx’(模板参数推导失败)

模板参数推导失败

为啥模板参数推导在 C++ 中不起作用?

采用 Eigen::Tensor 的函数 - 模板参数推导失败

C++ 中聚合的带括号初始化的模板参数推导

模板参数推导