With templates: Operator resolved first or convers

2019-07-18 18:58发布

问题:

I saw some interesting compiler behavior yesterday and I think I understand why it's happening, but I want to be sure. So, I'm not going to write my reasoning, just the facts.

Note, it's not a typo that I've included vector instead of string. I did it intentionally so that the compiler would not be able to understand what an std::string was and so that it would have to search around to figure out which operator I'm referring to by +:

#include <vector>
// #include <string> // intentionally commented out

template <typename T> struct A
{
    A() { };
    ~A() { };

    int m_member;
};

template <typename T> A<T> operator+(double lhs, const A<T> &rhs);

int main(int argc, char **argv)
{
    std::string fullString = std::string("Hi ") + std::string("mom!");
}

So, I get a slew of compiler errors in MS Visual Studio 2005. I'm only showing a subset of them.

1>.\test.cpp(21) : error C2784: 'A<T> operator +(double,const A<T> &)' : could not deduce template argument for 'const A<T> &' from 'std::basic_string<_Elem,_Traits,_Ax>'
1>        with
1>        [
1>            _Elem=char,
1>            _Traits=std::char_traits<char>,
1>            _Ax=std::allocator<char>
1>        ]
1>        .\test.cpp(16) : see declaration of 'operator +'

... errors continued ...

1>.\test.cpp(21) : error C2784: 'std::_Vb_iterator<_MycontTy> std::operator +(_Vb_iterator<_MycontTy>::difference_type,std::_Vb_iterator<_MycontTy>)' : could not deduce template argument for 'std::_Vb_iterator<_MycontTy>' from 'std::basic_string<_Elem,_Traits,_Ax>'
1>        with
1>        [
1>            _Elem=char,
1>            _Traits=std::char_traits<char>,
1>            _Ax=std::allocator<char>
1>        ]
1>        C:\Program Files (x86)\Microsoft Visual Studio 8\VC\include\vector(1800) : see declaration of 'std::operator +'

... errors continued ...

1>.\test.cpp(21) : error C2784: 'std::_Vb_const_iterator<_MycontTy> std::operator +(_Vb_const_iterator<_MycontTy>::difference_type,std::_Vb_const_iterator<_MycontTy>)' : could not deduce template argument for 'std::_Vb_const_iterator<_MycontTy>' from 'std::basic_string<_Elem,_Traits,_Ax>'
1>        with
1>        [
1>            _Elem=char,
1>            _Traits=std::char_traits<char>,
1>            _Ax=std::allocator<char>
1>        ]
1>        C:\Program Files (x86)\Microsoft Visual Studio 8\VC\include\vector(1695) : see declaration of 'std::operator +'

... errors continued ...

1>.\test.cpp(21) : error C2784: 'std::_Vector_iterator<_Ty,_Alloc> std::operator +(_Vector_iterator<_Ty,_Alloc>::difference_type,std::_Vector_iterator<_Ty,_Alloc>)' : could not deduce template argument for 'std::_Vector_iterator<_Ty,_Alloc>' from 'std::basic_string<_Elem,_Traits,_Ax>'
1>        with
1>        [
1>            _Elem=char,
1>            _Traits=std::char_traits<char>,
1>            _Ax=std::allocator<char>
1>        ]
1>        C:\Program Files (x86)\Microsoft Visual Studio 8\VC\include\vector(396) : see declaration of 'std::operator +'

... errors continued ...

1>.\test.cpp(21) : error C2784: 'std::_Vector_const_iterator<_Ty,_Alloc> std::operator +(_Vector_const_iterator<_Ty,_Alloc>::difference_type,std::_Vector_const_iterator<_Ty,_Alloc>)' : could not deduce template argument for 'std::_Vector_const_iterator<_Ty,_Alloc>' from 'std::basic_string<_Elem,_Traits,_Ax>'
1>        with
1>        [
1>            _Elem=char,
1>            _Traits=std::char_traits<char>,
1>            _Ax=std::allocator<char>
1>        ]
1>        C:\Program Files (x86)\Microsoft Visual Studio 8\VC\include\vector(264) : see declaration of 'std::operator +'

So, the compiler searches around for the what the + means and complains that it can't deduce the appropriate template arguments. I'm surprised by two related things, one is that it gives this error for each overloaded + operator that involves templates. This tells me that the compiler has absolutely no way of ruling out that any of these + are not meaningful; two, which is related, that it doesn't just complain that no suitable operator exists.

I see this as an opportunity to learn something about how templates are instantiated and compiled. Does anyone have any good explanation or references?

回答1:

In fact, since it couldn't find a match it's using the errors to tell you the possible operator candidates it actually found.

The g++ error for example tells you that it can't find the operator and then provides the same slew of messages, but in "candidates are:" form rather than one error per operator.

It's doing this in an attempt to be helpful: Since it couldn't find a match it assumes you may have actually meant any of the possible available operators.



回答2:

Note, it's not a typo that I've included vector instead of string. I did it intentionally so that the compiler would not be able to understand what an std::string was and so that it would have to search around to figure out which operator I'm referring to by +.

This is not correct. The compiler does know what std::string is. Consider, for example, that the following program compiles just fine (using Visual C++ 2010, but it should also work in 2005):

#include <vector>

int main() {
    std::string s;
}

When using Visual C++ (at least Visual C++ 2005 through 2010; I can't speak for other versions), including <vector> also includes whatever internal header declares and defines std::basic_string and std::string. This is permitted by the C++ langauge standard.

However, you apparently do not get the operator+ overload(s) that take std::string (or, more correctly, std::basic_string) operands. To get those you have to include <string>.

The compiler knows full well what std::string is, but when it attempts to find an operator+ overload for the two std::string objects, it fails and reports all of the operator+ overloads that it considered but rejected.



回答3:

that it doesn't just complain that no suitable operator exists.

That's IMO a plain stupid compiler behavior, if that's what happens. Surely it should first tell you what's wrong (i.e no suitable operator exists) and then list all the considered templates.

one is that it gives this error for each overloaded + operator that involves templates. This tells me that the compiler has absolutely no way of ruling out that any of these + are not meaningful;

In general, it cannot do this, because for example std::_Vector_iterator<_Ty,_Alloc> might be a base class of std::basic_string<>, which means that that one operator overload might be viable and deduction succeed. Argument deduction is the phase in compiling a C++ at that stage that figures these things out, and the compiler just reports the result of argument deduction to you.