Wrong function prototype used by compiler?

强颜欢笑 提交于 2019-12-08 21:43:03

问题


I faced a compilation problem that I do not understand, I have simplified it a bit for explanation below.

Basically, it involves having 2 different getters (a const and non-const one) that return a container (a map in this example) with const, respectively non-const value_type.

What puzzles me is that in the example below, the compiler seems unable to use the const getter on a non-const object:

#include "stdafx.h"
#include <utility>
#include <map>

class TestObject
{
public:

    TestObject() {}
    virtual ~TestObject() {}
};

typedef std::pair<const TestObject*, const TestObject*> ConstTestObjectPair;
typedef std::pair<TestObject*, TestObject*> TestObjectPair;

class Test
{
    TestObject* m_pObject;

public:

    Test() {m_pObject = new TestObject();}
    virtual ~Test() {delete m_pObject;}

    std::map<unsigned, ConstTestObjectPair> GetObject() const
    {
        std::map<unsigned, ConstTestObjectPair> map;
        map.insert(std::make_pair(0, std::make_pair(m_pObject, m_pObject)));
        return map;
    }

    std::map<unsigned, TestObjectPair> GetObject()
    {
        std::map<unsigned, TestObjectPair> map;
        map.insert(std::make_pair(0, std::make_pair(m_pObject, m_pObject)));
        return map;
    }
};

int _tmain(int argc, _TCHAR* argv[])
{
    Test* pTest = new Test();
    const Test* pConstTest = pTest;

    std::map<unsigned, ConstTestObjectPair> CTO = pTest->GetObject(); // Not compiling, I don't get why!!!
    CTO = pConstTest->GetObject();

    std::map<unsigned, TestObjectPair> TO = pTest->GetObject();
    //TO = pConstTest->GetObject(); // Not working, this is expected

    return 0;
}

I tried with both VS2010 and gcc and neither accepts to compile this code. Here is the compilation error returned by VS2010:

1>c:\test.cpp(48): error C2440: 'initializing' : cannot convert from 'std::map<_Kty,_Ty>' to 'std::map<_Kty,_Ty>'
1>          with
1>          [
1>              _Kty=unsigned int,
1>              _Ty=TestObjectPair
1>          ]
1>          and
1>          [
1>              _Kty=unsigned int,
1>              _Ty=ConstTestObjectPair
1>          ]
1>          No constructor could take the source type, or constructor overload resolution was ambiguous

Could someone explain me why the compiler cannot find/use the correct prototype on the non-const object?

Thanks a lot!


回答1:


If you're really curious, check out section 13.3.3 of the C++03 standard, which describes how the "best viable function" is determined. Here are some important points:

The selection criteria for the best function are the number of arguments, how well the arguments match the types of the parameters of the candidate function, how well (for nonstatic member functions) the object matches the implied object parameter, and certain other properties of the candidate function. [Note: the function selected by overload resolution is not guaranteed to be appropriate for the context. Other restrictions, such as the accessibility of the function, can make its use in the calling context ill-formed. ]

And later:

If there is exactly one viable function that is a better function than all other viable functions, then it is the one selected by overload resolution

Note that the return type of the function is not mentioned in this criteria. So the non-const method is selected as most valid, because its "implied object parameter" (essentially the "this" pointer) is non-const. This all happens before the conflict with the return type is detected.

To solve this problem, I would either:

  • Change your design so that ConstTestObjectPair isn't needed, and you can just use const TestObjectPair (preferred solution)
  • Cast your non-const objects to const ones when needed



回答2:


What puzzles me is that in the example below, the compiler seems unable to use the const getter on a non-const object

It is not "unable" but required to chose the other one.

Overloading is selected using the passed actual parameters. For member function including the hidden param used for this. For T* the non-const overload is selected, if you want the other you must use a const T* by cast or other means.

Actually it is a common mistake to think the return type will be used some way and the function that returns what you want to use in the expression gets selected. It is just not so.




回答3:


The problem is quite simple. pTest is a pointer to an object of type Test, which is not const. Hence in the call pTest->GetObject() the non-const member function is selected, that is

std::map<unsigned, TestObjectPair> GetObject()

As you see, this function returns a value of type std::map<unsigned, TestObjectPair>. But then you try to initialize the variable CTO of type

std::map<unsigned, ConstTestObjectPair>

with this value. To do that, the compiler needs to convert the returned value to this type. But there is no conversion constructor to do that. And that is what the compiler error tells you.




回答4:


C++ compiler will choose the explicit overrided method, so here pTest is a non-const viable and pConstTest is const one.

Test* pTest = new Test();
const Test* pConstTest = pTest;

pTest->GetObject will choose the non-const GetObject:

std::map<unsigned, TestObjectPair> GetObject()
{
    std::map<unsigned, TestObjectPair> map;
    map.insert(std::make_pair(0, std::make_pair(m_pObject, m_pObject)));
    return map;
}

pConstTest->GetObject() will choose the const GetObject:

std::map<unsigned, ConstTestObjectPair> GetObject() const
{
    std::map<unsigned, ConstTestObjectPair> map;
    map.insert(std::make_pair(0, std::make_pair(m_pObject, m_pObject)));
    return map;
}

the first error happened when you assign a returned

std::map<unsigned, TestObjectPair> value 

to a

std::map<unsigned, ConstTestObjectPair> viable


来源:https://stackoverflow.com/questions/16922121/wrong-function-prototype-used-by-compiler

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!