std::map<int, Complicated> m;
int anInt = 4;
double aDouble = 5.0;
std::string aString = "C++";
// cross your finger so that the optimizer is really good
m.insert(std::make_pair(4, Complicated(anInt, aDouble, aString)));
// should be easier for the optimizer
m.emplace(4, anInt, aDouble, aString);
那么,为什么他们没有在MSVC中实现正确的emplace_back版本呢?事实上,它也困扰了我一段时间前,所以我问了同样的问题上的Visual C++ blog。以下是微软Visual C++标准库实现的官方维护者Stephan T Lavavej的回答。 问:beta 2的emplace函数现在只是某种占位符吗? 答:正如你所知,可变模板在VC 10中没有实现。我们使用预处理器机制模拟它们,例如make_shared<T>(),tuple和<functional>中的新事物。这种预处理机相对难以使用和维护。而且,它会显著影响编译速度,因为我们必须重复包含子头。由于时间限制和编译速度的考虑,我们没有在emplace函数中模拟可变参数模板。 当可变参数模板在编译器中实现时,您可以期望我们将在库中利用它们,包括在我们的emplace函数中。我们非常认真地对待一致性,但不幸的是,我们不能一次性完成所有的事情。 这是个可以理解的决定每个尝试过一次用预处理器可怕的技巧来模拟variadic模板的人都知道这东西有多恶心。
std::vector<std::pair<std::string, std::string> > vec;
vec.emplace_back(std::string("Hello"), std::string("world"));
// should end up invoking this constructor:
//template<class U, class V> pair(U&& x, V&& y);
//without making any copies of the strings
emplace_back的优化可以在以下示例中演示。 对于emplace_back,将调用构造函数A (int x_arg)。 对于push_back,首先调用A (int x_arg),然后调用move A (A &&rhs)。 当然,构造函数必须标记为explicit,但对于当前示例,最好删除显式。
#include <iostream>
#include <vector>
class A
{
public:
A (int x_arg) : x (x_arg) { std::cout << "A (x_arg)\n"; }
A () { x = 0; std::cout << "A ()\n"; }
A (const A &rhs) noexcept { x = rhs.x; std::cout << "A (A &)\n"; }
A (A &&rhs) noexcept { x = rhs.x; std::cout << "A (A &&)\n"; }
private:
int x;
};
int main ()
{
{
std::vector<A> a;
std::cout << "call emplace_back:\n";
a.emplace_back (0);
}
{
std::vector<A> a;
std::cout << "call push_back:\n";
a.push_back (1);
}
return 0;
}
输出:
call emplace_back:
A (x_arg)
call push_back:
A (x_arg)
A (A &&)
// constructs the elements in place.
emplace_back("element");
// creates a new object and then copies (or moves) that object.
push_back(ExplicitDataType{"element"});
7条答案
按热度按时间nhn9ugyo1#
除了访客所说的:
MSCV 10提供的函数
void emplace_back(Type&& _Val)
是不一致和冗余的,因为正如您所指出的,它严格等效于push_back(Type&& _Val)
。但是
emplace_back
的真实的C++0x形式真的很有用:void emplace_back(Args&&...)
;它不是接受一个
value_type
,而是接受一个可变参数列表,所以这意味着你现在可以完美地转发参数并直接将一个对象构造到一个容器中,而根本不需要临时的。这很有用,因为无论RVO和move语义有多聪明,仍然存在
push_back
可能进行不必要的复制(或移动)的复杂情况。例如,使用std::map
的传统insert()
函数,您必须创建一个临时的,然后将其复制到std::pair<Key, Value>
中,然后将其复制到map中:那么,为什么他们没有在MSVC中实现正确的
emplace_back
版本呢?事实上,它也困扰了我一段时间前,所以我问了同样的问题上的Visual C++ blog。以下是微软Visual C++标准库实现的官方维护者Stephan T Lavavej的回答。问:beta 2的emplace函数现在只是某种占位符吗?
答:正如你所知,可变模板在VC 10中没有实现。我们使用预处理器机制模拟它们,例如
make_shared<T>()
,tuple和<functional>
中的新事物。这种预处理机相对难以使用和维护。而且,它会显著影响编译速度,因为我们必须重复包含子头。由于时间限制和编译速度的考虑,我们没有在emplace函数中模拟可变参数模板。当可变参数模板在编译器中实现时,您可以期望我们将在库中利用它们,包括在我们的emplace函数中。我们非常认真地对待一致性,但不幸的是,我们不能一次性完成所有的事情。
这是个可以理解的决定每个尝试过一次用预处理器可怕的技巧来模拟variadic模板的人都知道这东西有多恶心。
hi3rlvi22#
emplace_back
不应该接受vector::value_type
类型的参数,而是接受被转发到附加项的构造函数的可变参数。可以传递一个
value_type
,它将被转发到复制构造函数。因为它转发参数,这意味着如果你没有右值,这仍然意味着容器将存储“复制”的副本,而不是移动的副本。
但是上面的操作应该与
push_back
的操作相同。它可能更适用于以下用例:lf5gs5x23#
emplace_back
的优化可以在以下示例中演示。对于
emplace_back
,将调用构造函数A (int x_arg)
。对于
push_back
,首先调用A (int x_arg)
,然后调用move A (A &&rhs)
。当然,构造函数必须标记为
explicit
,但对于当前示例,最好删除显式。输出:
zqry0prt4#
再举一个列表的例子:
1l5u6lss5#
emplace_back
的具体用例:如果需要创建一个临时对象,然后将其推入容器,请使用emplace_back
而不是push_back
。它将在容器内就地创建对象。注意事项:
1.上面例子中的
push_back
将创建一个临时对象并将其移动到容器中。但是,用于emplace_back
的就地构造将比构造然后移动对象(通常涉及一些复制)更高效。1.一般来说,您可以在所有情况下使用
emplace_back
而不是push_back
,而不会有太大问题。(参见exceptions)icnyk63a6#
这里显示了push_back和emplace_back的代码。
http://en.cppreference.com/w/cpp/container/vector/emplace_back
您可以在push_back上看到move操作,而在emplace_back上看不到。
yfwxisqw7#
符合
emplace_back
的实现将在添加到vector时将参数转发到vector<Object>::value_type
构造函数。我记得Visual Studio不支持可变模板,但Visual Studio 2013 RC将支持可变模板,所以我猜会添加一个符合的签名。对于
emplace_back
,如果直接将参数转发给vector<Object>::value_type
构造函数,严格来说,emplace_back
函数不需要可移动或可复制的类型。在vector<NonCopyableNonMovableObject>
的情况下,这是没有用的,因为vector<Object>::value_type
需要一个可复制或可移动的类型来增长。但是 * 注意 *,这对
std::map<Key, NonCopyableNonMovableObject>
可能很有用,因为一旦你在map中分配了一个条目,它就不需要再被移动或复制了,这与vector
不同,这意味着你可以有效地将std::map
用于一个既不可复制也不可移动的Map类型。