【问题标题】:Conversion operator is not working for function parameter转换运算符不适用于函数参数
【发布时间】:2013-09-05 16:21:03
【问题描述】:

为什么这段代码无法编译?

#include <iostream>
#include <vector>

template<class T>
class vector_ref
{
public:
    vector_ref(T *pData, int pN) {Data = pData; N = pN;};
    T *Data;
    int N;
    vector_ref<T>& operator=(const std::vector<T> &v1)
    {
        for(int ii = 0; ii < N; ii++)
        {
            Data[ii] = v1[ii];
        }
        return *this;
    };
    operator std::vector<T>()
    {
        std::vector<T> v1(N);
        for(int ii = 0; ii < N; ii++)
        {
            v1[ii] = Data[ii];
        }
        return v1;
    };
};

template<class T>
void printVec(std::vector<T> v1)
{
    for(int ii = 0; ii < v1.size(); ii++)
    {
        std::cout << v1[ii] << std::endl;
    }
}

int main()
{
    std::vector<double> v;
    v.push_back(1.0);
    v.push_back(2.0);
    v.push_back(3.0);

    vector_ref<double> v_ref(&v[0],3);
    printVec(v_ref); // Compiler error

    return 0;
}

我正在使用g++ 4.7.3 使用以下命令进行编译:g++ test.cpp。错误信息是:

test.cpp: In function ‘int main()’:
test.cpp:56:19: error: no matching function for call to ‘printVec(vector_ref<double>&)’
test.cpp:56:19: note: candidate is:
test.cpp:40:6: note: template<class T> void printVec(std::vector<T>)
test.cpp:40:6: note:   template argument deduction/substitution failed:
test.cpp:56:19: note:   ‘vector_ref<double>’ is not derived from ‘std::vector<T>’

this answer to a previous question似乎暗示这应该可行。

【问题讨论】:

  • 我认为您需要void printVec(T v) 而不是参数列表中的std::vector&lt;T&gt;。但是,该函数内部的操作将失败。
  • 这给出了错误:error: ‘class vector_ref&lt;double&gt;’ has no member named ‘size’ 在函数 printVec() 中。我可以将v1 分配给函数内部的vector,但我不想这样做。
  • 是的。这就是我在上一条评论中间接指出的。您的vector_ref 没有名为size() 的成员函数。您可以编写自己的函数并实现您想要的功能。
  • operator std::vector&lt;T&gt;&amp;() 不返回对局部变量的引用吗?
  • 在我正在处理的代码中,我有大量类似于printVec() 的函数。我宁愿让它们保持原样,并在函数调用中将vector_ref 转换为std::vector。如果没有办法做到这一点,我可以改变功能......这不是我的第一选择。

标签: c++ implicit-conversion conversion-operator


【解决方案1】:

正如错误消息所说:

test.cpp:56:19: error: no matching function for call to ‘printVec(vector_ref<double>&)’

果然,那一行是:

vector_ref<double> v_ref(&v[0],3);
printVec(v_ref); // Compiler error

请注意,v_refvector_ref&lt;double&gt;。现在,错误消息有用地指出存在printVec 函数,但它有所不同:

test.cpp:56:19: note: candidate is:
test.cpp:40:6: note: template<class T> void printVec(std::vector<T>)

如果我们转到第 40 行并查看 printVec 函数,您将看到:

template<class T>
void printVec(std::vector<T> v1)

所以,这就是这个意思:

  1. printVec 将std::vector&lt;T&gt; 作为参数。
  2. 您使用 vector_ref&lt;double&gt; 作为参数调用它。
  3. 这些是完全不同的类型,所以它失败了。

这就是错误消息的含义。

现在,我看到您正在尝试制作可以隐式转换为矢量的东西。由于模板,这变得混乱。这种方法适用于包装非模板类型,但模板有问题,原因如下:

当编译器试图处理printVec(v_ref) 时,它必须找到这样一个printVec 的声明。它寻找需要vector_ref&lt;double&gt; 的东西,但没有找到任何东西。它确实找到了一个模板函数,因此它尝试查看是否可以为此类型实例化模板函数。 printVec 的签名是它需要std::vector&lt;T&gt;,与vector_ref&lt;double&gt; 不匹配,所以它不匹配,它继续前进。这真的就像“不匹配,放弃并继续前进”一样简单。它不会尝试对您的类型进行任何转换。

要解决此问题,您可以按照 Sebastian 的建议添加显式 .toVector()。或者,它可能会显式实例化模板方法:

template<class T>
void printVec(std::vector<T> v1)
{
    for(int ii = 0; ii < v1.size(); ii++)
    {
        std::cout << v1[ii] << std::endl;
    }
}
template<> void printVec(std::vector<double> v1);  // explicit instantiation 

这明确告诉编译器为std::vector&lt;double&gt; 实例化模板方法,然后当它试图找到printVec(vector_ref&lt;double&gt;) 的匹配项时,它将看到两个选项——模板方法和实例化方法。模板方法将像以前一样失败,但它可能会意识到它可以进行隐式转换以使用实例化方法。这可能有效,但我还没有测试过。

我不确定这是否可行,.toVector() 绝对更干净。但是显式模板实例化是一个巧妙的技巧,并且偶尔有用,所以我想我会提到它。

【讨论】:

  • 显式实例化不起作用。但是如果你把它放在模板旁边的标题中,当你包含来自两个不同 CU 的文件时,它导致链接器错误。
【解决方案2】:

您对 vector& 的隐式转换是不安全的,并且会做可怕的事情。去掉它。并打开你的编译器警告,因为编译器应该已经对你大喊大叫了。

编译器错误背后的问题是参数推导没有考虑转换;它在参数类型和参数类型模式之间进行严格的模式匹配。并且vector&lt;T&gt;vector_ref&lt;double&gt;之间没有匹配。

您无法使这条线正常工作。要么给vector_ref一个向量的完整接口,要么让printVec成为一个完整的模板,或者使用显式转换或显式转换函数,例如v_ref.to_vector().

【讨论】:

    【解决方案3】:

    查看 Sebastian Redl 和 Tim 的答案,了解编译失败的原因


    您可以重载 (): 类似于 Sebastian Redl 建议的 to_vector 函数

     std::vector<T> operator() ()
    {
        std::vector<T> v1(N);
        for(int ii = 0; ii < N; ii++)
        {
            v1[ii] = Data[ii];
        }
        return v1;
    }
    

    然后使用

    printVec(v_ref());

    here

    【讨论】:

    • 如果你要这样做,你应该简单地创建一个命名方法,例如to_vector()。这将更加清晰和可读。 Operator() 真的不应该这样使用。
    • 我同意蒂姆的观点。使用函数调用运算符进行显式类型转换是一种滥用,一开始就给运算符重载带来了坏名声。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-06-04
    • 2012-10-08
    • 2019-02-04
    • 2014-02-18
    • 1970-01-01
    • 2021-06-30
    • 1970-01-01
    相关资源
    最近更新 更多