What is the purpose of std::make_pair vs the constructor of std::pair?

后端 未结 7 1271
借酒劲吻你
借酒劲吻你 2020-12-04 06:04

What is the purpose of std::make_pair?

Why not just do std::pair(0, \'a\')?

Is there any difference between the tw

相关标签:
7条回答
  • 2020-12-04 06:18

    As @MSalters replied above, you can now use curly braces to do this in C++11 (just verified this with a C++11 compiler):

    pair<int, int> p = {1, 2};
    
    0 讨论(0)
  • 2020-12-04 06:18

    make_pair creates an extra copy over the direct constructor. I always typedef my pairs to provide simple syntax.
    This shows the difference (example by Rampal Chaudhary):

    class Sample
    {
        static int _noOfObjects;
    
        int _objectNo;
    public:
        Sample() :
            _objectNo( _noOfObjects++ )
        {
            std::cout<<"Inside default constructor of object "<<_objectNo<<std::endl;
        }
    
        Sample( const Sample& sample) :
        _objectNo( _noOfObjects++ )
        {
            std::cout<<"Inside copy constructor of object "<<_objectNo<<std::endl;
        }
    
        ~Sample()
        {
            std::cout<<"Destroying object "<<_objectNo<<std::endl;
        }
    };
    int Sample::_noOfObjects = 0;
    
    
    int main(int argc, char* argv[])
    {
        Sample sample;
        std::map<int,Sample> map;
    
        map.insert( std::make_pair( 1, sample) );
        //map.insert( std::pair<int,Sample>( 1, sample) );
        return 0;
    }
    
    0 讨论(0)
  • 2020-12-04 06:29

    Class template arguments could not be inferred from the constructor before C++17

    Before C++17 you could not write something like:

    std::pair p(1, 'a');
    

    since that would infer template types from the constructor arguments, you had to write it explicitly as:

    std::pair<int,char> p(1, 'a');
    

    C++17 makes that syntax possible, and therefore make_pair redundant.

    Before C++17, std::make_pair allowed us to write less verbose code:

    MyLongClassName1 o1;
    MyLongClassName2 o2;
    auto p = std::make_pair(o1, o2);
    

    instead of the more verbose:

    std::pair<MyLongClassName1,MyLongClassName2> p{o1, o2};
    

    which repeats the types, and can be very long.

    Type inference works in that pre-C++17 case because make_pair is not a constructor.

    make_pair is essentially equivalent to:

    template<class T1, class T2>
    std::pair<T1, T2> my_make_pair(T1 t1, T2 t2) {
        return std::pair<T1, T2>(t1, t2);
    }
    

    The same concept applies to inserter vs insert_iterator.

    See also:

    • Why not infer template parameter from constructor?
    • https://en.wikibooks.org/wiki/More_C++_Idioms/Object_Generator

    Minimal example

    To make things more concrete, we can observe the problem minimally with:

    main.cpp

    template <class MyType>
    struct MyClass {
        MyType i;
        MyClass(MyType i) : i(i) {}
    };
    
    template<class MyType>
    MyClass<MyType> make_my_class(MyType i) {
        return MyClass<MyType>(i);
    }
    
    int main() {
        MyClass<int> my_class(1);
    }
    

    then:

    g++-8 -Wall -Wextra -Wpedantic -std=c++17 main.cpp
    

    compiles happily, but:

    g++-8 -Wall -Wextra -Wpedantic -std=c++14 main.cpp
    

    fails with:

    main.cpp: In function ‘int main()’:
    main.cpp:13:13: error: missing template arguments before ‘my_class’
         MyClass my_class(1);
                 ^~~~~~~~
    

    and requires instead to work:

    MyClass<int> my_class(1);
    

    or the helper:

    auto my_class = make_my_class(1);
    

    which uses a regular function instead of a constructor.

    Difference for std::reference_wrapper

    This comment mentions that std::make_pair unwraps std::reference_wrapper while the constructor does not, so that's one difference. TODO example.

    Tested with GCC 8.1.0, Ubuntu 16.04.

    0 讨论(0)
  • 2020-12-04 06:33

    The difference is that with std::pair you need to specify the types of both elements, whereas std::make_pair will create a pair with the type of the elements that are passed to it, without you needing to tell it. That's what I could gather from various docs anyways.

    See this example from http://www.cplusplus.com/reference/std/utility/make_pair/

    pair <int,int> one;
    pair <int,int> two;
    
    one = make_pair (10,20);
    two = make_pair (10.5,'A'); // ok: implicit conversion from pair<double,char>
    

    Aside from the implicit conversion bonus of it, if you didn't use make_pair you'd have to do

    one = pair<int,int>(10,20)
    

    every time you assigned to one, which would be annoying over time...

    0 讨论(0)
  • 2020-12-04 06:36

    starting from c++11 just use uniform initialization for pairs. So instead of:

    std::make_pair(1, 2);
    

    or

    std::pair<int, int>(1, 2);
    

    just use

    {1, 2};
    
    0 讨论(0)
  • 2020-12-04 06:39

    It's worth noting that this is a common idiom in C++ template programming. It's known as the Object Generator idiom, you can find more information and a nice example here.

    Edit As someone suggested in the comments (since removed) the following is a slightly modified extract from the link in case it breaks.

    An Object Generator allows creation of objects without explicitly specifying their types. It is based on a useful property of function templates which class templates don't have: The type parameters of a function template are deduced automatically from its actual parameters. std::make_pair is a simple example that returns an instance of the std::pair template depending on the actual parameters of the std::make_pair function.

    template <class T, class U>
    std::pair <T, U> 
    make_pair(T t, U u)
    {
      return std::pair <T, U> (t,u);
    }
    
    0 讨论(0)
提交回复
热议问题