std::make_pair with c++ 11

2020-02-06 02:30发布

问题:

Hello I have the following code:

bool PinManager::insertPin(const std::string& p_pinNumber, const std::string& p_mode)
{
    boost::shared_ptr<GPIOPin> pin(new GPIOPin(p_pinNumber, p_mode));
    if (pin)
    {
        m_pinsInUse.insert(std::make_pair<std::string, boost::shared_ptr<GPIOPin> >(p_pinNumber, pin));
        return true;
    }
    return false;
}

This code has always compiled, but when I added the -std=c++0x flag this code fails to compile with the message:

[ 42%] Building CXX object gpioaccess/CMakeFiles/gpioaccess.dir/pinmanager/pinmanager.cpp.o
/home/pi/robot_2.0/trunk/gpioaccess/pinmanager/pinmanager.cpp: In member function 'bool gpioaccess::PinManager::insertPin(const string&, const string&)':
/home/pi/robot_2.0/trunk/gpioaccess/pinmanager/pinmanager.cpp:39:101: error: no matching function for call to 'make_pair(const string&, boost::shared_ptr<gpioaccess::GPIOPin>&)'
/home/pi/robot_2.0/trunk/gpioaccess/pinmanager/pinmanager.cpp:39:101: note: candidate is:
/usr/include/c++/4.6/bits/stl_pair.h:262:5: note: template<class _T1, class _T2> std::pair<typename std::__decay_and_strip<_T1>::__type, typename std::__decay_and_strip<_T2>::__type> std::make_pair(_T1&&, _T2&&)
gpioaccess/CMakeFiles/gpioaccess.dir/build.make:77: recipe for target 'gpioaccess/CMakeFiles/gpioaccess.dir/pinmanager/pinmanager.cpp.o' failed
make[2]: *** [gpioaccess/CMakeFiles/gpioaccess.dir/pinmanager/pinmanager.cpp.o] Error 1
CMakeFiles/Makefile2:75: recipe for target 'gpioaccess/CMakeFiles/gpioaccess.dir/all' failed
make[1]: *** [gpioaccess/CMakeFiles/gpioaccess.dir/all] Error 2
Makefile:75: recipe for target 'all' failed
make: *** [all] Error 2

After doing a little digging I found that the fact this compiled before was probably a bug; however, I am still unsure how to fix this. Does anyone have any points in the correct direction?

gcc --version is gcc (Debian 4.6.3-14+rpi1) 4.6.3

回答1:

std::make_pair has changed between c++03 and c++11.

In c++11, it accepts parameters by forwarding-references rather than by value. This means that by specifying its type template arguments explicitly you end up with the following instantiation:

std::make_pair<std::string , boost::shared_ptr<GPIOPin> >
             (std::string&&, boost::shared_ptr<GPIOPin>&&);

expecting rvalue references, which cannot bind to lvalues.

You should not specify type template arguments of std::make_pair explicitly. Instead, you should let the compiler deduce them on its own:

std::make_pair(p_pinNumber, pin)

Additionally, since c++11, when using std::map, you can construct the pair in-place:

m_pinsInUse.emplace(p_pinNumber, pin);

In c++17 you can utilize class template argument deduction, and construct std::pair without specifying class template arguments:

std::pair(p_pinNumber, pin)


回答2:

The whole point of std::make_pair is to simplify creating a std::pair so you don't need to specify the template arguments, letting the compiler deduce them.

If you call std::make_pair<T, U>(t, u) with explicit template arguments then you are defeating the entire purpose of the function, because you are preventing deduction, and you're wasting time typing extra characters and potentially creating unnecessary copies of objects, when you could just do std::pair<T, U>(t, u) instead (saving yourself from typing five extra, useless characters).

If you use std::make_pair the way it's intended to be used then you won't even notice that it changed in C++11. If you misuse it by pointlessly providing template arguments then it might not work in C++11, so don't do that.

I wrote a more complete explanation at http://advogato.org/person/redi/diary/239.html



回答3:

Note that in c++11 std::make_pair can often be dropped in favour of the initializer constructor

m_pinsInUse.insert({p_pinNumber, pin});


回答4:

In addition to the answer @NexusSquared provided: you could just use the new emplace function of std::map:

m_pinsInUse.emplace(p_pinNumber, pin);

which is even shorter and constructs the std::pair inplace instead of constructing it followed by copying the contents of the newly constructed pair to the memory of the map.