【问题标题】:C++ - Template argument deduction/substitution failedC++ - 模板参数推导/替换失败
【发布时间】:2014-09-22 19:14:27
【问题描述】:

我的目标是能够在 std::vector 上使用算术运算符。考虑以下示例:

#include <vector>

using namespace std;

template <class S, class T, class U> vector<U> operator+(const vector<S> &a, const vector<T> &b){
    vector<U> result;
    result.reserve(a.size());
    for(int i = 0; i < a.size();++i){
        result[i] = a[i] + b[i];
    }
    return result;
}

int main(int argc, char** argv) {
    vector<double> bla;
    bla = bla + bla;
    return 0;
}

此代码无法编译,因为编译器无法推断出模板参数 U(它不是 MWE,但我试图提供一个有意义的示例)。为什么会这样?我知道在这里使用三个模板参数可能没有意义。我的想法是,在类型 S 和 T 都提供具有不同返回类型的匹配“+”实现的情况下,我可以同时处理这两种情况。还是会出现模棱两可的问题? 我只是想知道编译器是否应该能够推断出 U。当然,以下代码可以正常工作:

#include <vector>

using namespace std;

template <class S, class T> vector<S> operator+(const vector<S> &a, const vector<T> &b){
    vector<S> result;
    result.reserve(a.size());
    for(int i = 0; i < a.size();++i){
        result[i] = a[i] + b[i];
    }
    return result;
}

int main(int argc, char** argv) {
    vector<double> bla;
    bla = bla + bla;
    return 0;
}

【问题讨论】:

  • 你可能对std::common_type感兴趣:en.cppreference.com/w/cpp/types/common_type
  • 这是一个骗子,但我找不到。基本上,您对结果所做的操作对重载决议没有任何影响。因此,它正在尝试编译bla+bla,但是那里没有足够的信息来推断模板参数U
  • C++ 的做法是使用std::transform
  • std::valarray 已经实现了这个。

标签: c++ templates


【解决方案1】:

您可以使用common type 完成此操作

#include <vector>
#include <type_traits>

using namespace std;

template <class S, class T> 
vector<typename std::common_type<S, T>::type> operator+(const vector<S> &a, const vector<T> &b)
{
    vector<typename std::common_type<S, T>::type> result;
    result.reserve(a.size());
    for(unsigned int i = 0; i < a.size();++i){
        result[i] = a[i] + b[i];
    }
    return result;
}

int main() {
    vector<double> bla;
    bla = bla + bla;
    return 0;
}

Live Example

编辑: 正如 Jarod42 建议的那样,您也可以使用 vector&lt;decltype(a[0] + b[0])&gt; 作为另一种可能的返回类型(可能与 common_type 不同)。请记住,后一种需要尾随返回类型或std::declval (C++11)

【讨论】:

  • 此处无需尾随返回类型即可与 C++03 兼容。
  • vector&lt;decltype(a[0] + b[0])&gt; 可能是其他可能的返回类型(可能与 common_type 不同)(这需要尾随返回类型或std::declval 等等 C++ 11)
  • 这也很好,为了完整起见,我将其添加到答案中。谢谢
【解决方案2】:

Marco A. 的解决方案很好,我想提供更多细节,并说明为什么 Macro A 的答案更好。

解决方案 1: 由于您将 operator+ 的返回类型作为第三个参数,所以当显式声明模板参数时,我们必须指定所有三个参数,如下所示:

#include <iostream>     // std::cout
#include <iterator>     // std::ostream_iterator
#include <vector>       // std::vector
#include <algorithm>    // std::copy

// declare return type as the 3rd parameter
template <class S, class T, class U> std::vector<U> operator+(const std::vector<S> &a, const std::vector<T> &b){
    std::vector<U> result(a.size());
    for(int i = 0; i < a.size();++i){
        result[i] = a[i] + b[i];
    }
    return result;
}

int main(int argc, char** argv) {
    std::vector<double> bla;
    bla.push_back(3.14);
    bla.push_back(2.7);

    // explicity specify three  template parameter
    bla = ::operator+<double,double,double>(bla,bla); 

    // print results   
    std::ostream_iterator<double> os_it(std::cout," ");
    std::copy(bla.begin(),bla.end(),os_it);
    std::cout << std::endl;

    return 0;
}

输出为:

6.28 5.4

解决方案2:你可以将返回类型作为第一个参数,然后我们只需要指定返回类型,其他两个参数可以由编译器推导出来。

#include <iostream>     // std::cout
#include <iterator>     // std::ostream_iterator
#include <vector>       // std::vector
#include <algorithm>    // std::copy

//decalre return type as first template parameter
template <class U, class S, class T> std::vector<U> operator+(const std::vector<S> &a, const std::vector<T> &b){
    std::vector<U> result(a.size());
    for(int i = 0; i < a.size();++i){
        result[i] = a[i] + b[i];
    }
    return result;
}

int main(int argc, char** argv) {
    std::vector<double> bla;
    bla.push_back(3.14);
    bla.push_back(2.7);

    // only explicity specify the return type 
    bla = ::operator+<double>(bla,bla);

    // print results   
    std::ostream_iterator<double> os_it(std::cout," ");
    std::copy(bla.begin(),bla.end(),os_it);
    std::cout << std::endl;

    return 0;
}

输出为:

6.28 5.4 

解决方案 3: 正如 Macro A. 给出的那样,这比上述两个解决方案要好。我们不必明确指定返回类型。作为一个完整的示例,我还将我的可运行示例代码放在这里:

//Note:  This file should be compiled with c++11

#include <iostream>     // std::cout
#include <iterator>     // std::ostream_iterator
#include <vector>       // std::vector
#include <algorithm>    // std::copy
#include <type_traits>  // std::common_type c++11

//using std::common_type 
template <class S, class T> std::vector<typename std::common_type<S, T>::type> operator+(const std::vector<S> &a, const std::vector<T> &b){
    std::vector<typename std::common_type<S, T>::type> result(a.size());
    for(int i = 0; i < a.size();++i){
        result[i] = a[i] + b[i];
    }
    return result;
}

// print results   
template <typename T>
void printData(const std::vector<T>& bla) {
    std::ostream_iterator<double> os_it(std::cout," ");
    std::copy(bla.begin(),bla.end(),os_it);
    std::cout << std::endl;
}


int main(int argc, char** argv) {
    std::vector<double> dvec;
    dvec.push_back(3.14);
    dvec.push_back(2.7);

    std::vector<int> ivec;
    ivec.push_back(1);
    ivec.push_back(2);

    // we need not to  explicity specify template parameter 
    printData(dvec+dvec);
    printData(dvec+ivec);
    printData(ivec+ivec); 

    return 0;
}

输出为:

6.28 5.4 
4.14 4.7 
2 4 

【讨论】:

    【解决方案3】:

    编译器如何推导出U?没有提供任何关于U 的信息。

    将其更改为只有ST,并使返回类型取决于ST 之间的某种关系。

    例如。

    template<typename S, typename T>
    auto operator+(vector<S> const &a, vector<T> const &b) -> vector< foo<S,T> >
    

    【讨论】:

    • 对于新手来说,bla = 似乎应该提供推断U 的信息。该信息提供的,它只是被编译器忽略(根据规范应该如此)。
    • 新手可以学习表达式必须有类型而不管其内容的原理; bla + bla 的定义相同,无论您是否将其包含在更大的表达式中。 :)
    • 感谢您的回答,我认为您的回答与 Marco 的方向相同。 @MooingDuck,我知道这一点。我宁愿认为 'a[i] + b[i]' 应该提供必要的信息。我应该更清楚地说明这一点。
    【解决方案4】:

    标准已经提供了基于算术的数组类std::valarray

    std::valarray<double> bla{2.0, 4.0, 6.0};
    bla = bla + bla;
    

    【讨论】:

    • OP 专门要求提供一个矢量,但无论如何这很酷:)
    猜你喜欢
    • 2018-04-28
    • 1970-01-01
    • 1970-01-01
    • 2015-07-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-04-03
    • 2021-01-24
    相关资源
    最近更新 更多