سؤال

Why does this code not compile?

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

I am compiling with g++ 4.7.3 using the command: g++ test.cpp. The error message is:

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 seems to suggest that this should work.

هل كانت مفيدة؟

المحلول

As the error message says:

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

Sure enough, that line is:

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

Note that v_ref is a vector_ref<double>. Now, the error message helpfully points out that there is a printVec function, but it's different:

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

And if we go to line 40 and look at the printVec function, you will see:

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

So, here's what this means:

  1. printVec takes a std::vector<T> as an argument.
  2. You're calling it with a vector_ref<double> as an argument.
  3. Those are completely different types, so it fails.

So that's what the error message means.

Now, I see that you're trying to make something that can be implicitly converted to a vector. This gets messy because of the templates. This approach works for wrapping non-template types, but has trouble with templates, and here's why:

When the compiler is trying to deal with printVec(v_ref), it has to find the declaration for such a printVec. It looks for something that takes vector_ref<double>, but doesn't find anything. It does find a template function, so then it tries to see if the template function can be instantiated for this type. The signature for printVec is that it takes std::vector<T>, and that doesn't match vector_ref<double>, so it doesn't match, and it moves on. It really is as simple as "doesn't match, give up and move on". It won't try to do any conversions on your type.

To solve this, you can add an explicit .toVector() as Sebastian suggests. Or, it might work to explicitly instantiate the template method:

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 

That explicitly tells the compiler to instantiate the template method for std::vector<double>, and then when it's trying to find a match for printVec(vector_ref<double>), it will see two options - a template method and an instantiated method. The template method will fail as before, but it might realize that it can do an implicit conversion to use the instantiated method. This might work, but I haven't tested it.

I'm not sure if that will work, and .toVector() is definitely cleaner. But explicit template instantiation is a neat trick, and occasionally useful, so I thought I'd mention it.

نصائح أخرى

Your implicit conversion to vector& is unsafe and will do horrible things. Remove it. And turn up your compiler warnings, because the compiler should have screamed at you for it already.

The problem behind your compiler error is that argument deduction doesn't take conversions into account; it does strict pattern matching between the argument type and the parameter type pattern. And there is no match between vector<T> and vector_ref<double>.

There is no way you can make this line work. Either give vector_ref the full interface of a vector and make printVec a full template, or use an explicit cast or an explicit conversion function, e.g. v_ref.to_vector().

See Sebastian Redl & Tim answer for why it failed to compile


You can overload (): similar to to_vector function as suggested by Sebastian Redl

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

then uses

printVec(v_ref());

See here

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top