std::make_pair 与 std::pair 构造函数的目的是什么?

发布于 2025-01-05 05:11:11 字数 122 浏览 2 评论 0原文

std::make_pair 的用途是什么?

为什么不直接执行 std::pair(0, 'a') 呢?

这两种方法有什么区别吗?

What is the purpose of std::make_pair?

Why not just do std::pair<int, char>(0, 'a')?

Is there any difference between the two methods?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

忘羡 2025-01-12 05:11:11

(由于 CTAD,此答案仅适用于 C++14 及更早的标准)< /strong>

区别在于,使用 std::pair 您需要指定两个元素的类型,而 std::make_pair 将创建一个对传递给它的元素的类型,而不需要告诉它。无论如何,这就是我可以从各种文档中收集到的内容。

请参阅 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>

必须执行此操作,随着时间的推移,这会很烦人......

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

除了它的隐式转换奖励之外,如果您不使用 make_pair,则每次分配给一个时都

(This answer is only correct for C++14 and earlier standards, due to CTAD)

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...

坏尐絯 2025-01-12 05:11:11

在 C++17 之前,无法从构造函数推断出类模板参数

在 C++17 之前,您不能编写如下内容:

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

因为这会从构造函数参数推断模板类型,所以您必须编写它明确为:

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

C++17 使该语法成为可能,因此 make_pair 是多余的。

在 C++17 之前,std::make_pair 允许我们编写更少冗长的代码:

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

而不是更冗长的代码:

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

它会重复类型,并且可能会很长。

类型推断适用于 C++17 之前的情况,因为 make_pair 不是构造函数。

make_pair 本质上等同于:

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

相同的概念适用于 inserterinsert_iterator

另请参阅:

最小示例

为了使事情更具体,我们可以用以下代码最小化地观察问题: :

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

愉快地编译,但是:

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

失败了:

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

并且需要改为工作:

MyClass<int> my_class(1);

或 helper:

auto my_class = make_my_class(1);

它使用常规函数而不是构造函数。

std::reference_wrapper 的差异

这个comment 提到 std::make_pair 解包 std::reference_wrapper 而构造函数则不然,所以这是一个区别。待办事项示例。

使用 GCC 8.1.0、Ubuntu 16.04

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:

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.

梦里寻她 2025-01-12 05:11:11

正如 @MSalters 上面回复的那样,您现在可以使用大括号在 C++11 中执行此操作(刚刚使用 C++11 编译器验证了这一点):

pair<int, int> p = {1, 2};

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};
云朵有点甜 2025-01-12 05:11:11

使用 make_pair 和使用指定类型参数显式调用 pair 构造函数没有区别。当类型很详细时,std::make_pair 会更方便,因为模板方法会根据给定的参数进行类型推导。
例如,

std::vector< std::pair< std::vector<int>, std::vector<int> > > vecOfPair;
std::vector<int> emptyV;

// shorter
vecOfPair.push_back(std::make_pair(emptyV, emptyV));

 // longer
vecOfPair.push_back(std::pair< std::vector<int>, std::vector<int> >(emptyV, emptyV));

There is no difference between using make_pair and explicitly calling the pair constructor with specified type arguments. std::make_pair is more convenient when the types are verbose because a template method has type deduction based on its given parameters.
For example,

std::vector< std::pair< std::vector<int>, std::vector<int> > > vecOfPair;
std::vector<int> emptyV;

// shorter
vecOfPair.push_back(std::make_pair(emptyV, emptyV));

 // longer
vecOfPair.push_back(std::pair< std::vector<int>, std::vector<int> >(emptyV, emptyV));
我家小可爱 2025-01-12 05:11:11

值得注意的是,这是 C++ 模板编程中的常见习惯用法。它被称为对象生成器习惯用法,您可以在此处找到更多信息和一个很好的示例。

编辑正如有人在评论中建议的那样(已删除),以下是对链接的稍微修改的摘录,以防链接中断。

对象生成器允许创建对象而无需显式指定其类型。它基于类模板所没有的函数模板的一个有用属性:函数模板的类型参数是从其实际参数自动推导出来的。 std::make_pair 是一个简单的示例,它根据 std::make_pair 的实际参数返回 std::pair 模板的实例> 功能。

template <class T, class U>
std::pair <T, U> 
make_pair(T t, U u)
{
  return std::pair <T, U> (t,u);
}

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);
}
不语却知心 2025-01-12 05:11:11

make_pair 在直接构造函数上创建一个额外的副本。我总是用 typedef 来提供简单的语法。
这显示了差异(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;
}

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;
}
莫言歌 2025-01-12 05:11:11

从 c++11 开始,只对对使用统一初始化。所以而不是:

std::make_pair(1, 2);

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

只是使用

{1, 2};

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};
○闲身 2025-01-12 05:11:11

我永远不会再使用 std::make_pair ,因为它已经“损坏”了(从我的角度来看 - 至少它不直观)。考虑以下示例

您有一个后备存储,或者一个字符串池

using Id = int;
std::vector<std::string> string_pool;
std::map<std::string_view, Id> str_to_id_map;

如果您尝试像这样插入到 std::map 中;

str_to_id_map.insert(std::make_pair(string_pool.back(), 42));

这将会失败,而且非常失败。它将首先创建 std::string 的副本,然后将其转换为 std::string_view > - 现在, string_view 指向“任何地方”。它可能有效,也可能无效。我最近刚刚测试过这个。

但是,如果您改为使用,

str_to_id_map.insert({string_pool.back(), 42});

编译器上帝将正确地将其推断为 string_pool 内容上的 string_view 并且可以工作 - 没有 std::string 的中间副本,变成 string_view

我从中得到的教训或收获是 - 永远、永远、永远、永远使用 std::make_pair。

I'm never going to use std::make_pair again because it's "broken" (from my perspective - at least it's unintuitive). Consider the following example

You have a backing storage, or a string pool of

using Id = int;
std::vector<std::string> string_pool;
std::map<std::string_view, Id> str_to_id_map;

If you attempt to insert into std::map like so;

str_to_id_map.insert(std::make_pair(string_pool.back(), 42));

This will fail, spectacularly. It will first create a copy of the std::string which then will be turned into a std::string_view - and now, that string_view is pointing to "anywhere". It might work, it might not work. I tested this just recently.

However, if you instead use

str_to_id_map.insert({string_pool.back(), 42});

This will correctly be deduced by the Compiler Gods as a string_view over the string_pool contents and will work - no intermediary copy of a std::string, turned into a string_view

My lesson or takeaway I gather from this is - never, ever, ever, ever use std::make_pair.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文