VS2010中函子(副本、赋值)的不同行为(与VS2005相比)

发布于 2024-08-25 07:27:39 字数 4168 浏览 1 评论 0原文

当从 VS2005 迁移到 VS2010 时,我们注意到性能下降,这似乎是由函子的额外副本引起的。

下面的代码说明了这个问题。拥有一个值本身就是一个集合的映射是至关重要的。在地图和集合上,我们定义了一个比较函子(在示例中是模板化的)。

#include <iostream>
#include <map>
#include <set>

class A
{
public:
   A(int i, char c) : m_i(i), m_c(c)
   {
   std::cout << "Construct object " << m_c << m_i << std::endl;
   }

   A(const A &a) : m_i(a.m_i), m_c(a.m_c)
   {
   std::cout << "Copy object " << m_c << m_i << std::endl;
   }

   ~A()
   {
   std::cout << "Destruct object " << m_c << m_i << std::endl;
   }

   void operator= (const A &a)
   {
   m_i = a.m_i;
   m_c = a.m_c;
   std::cout << "Assign object " << m_c << m_i << std::endl;
   }

   int m_i;
   char m_c;

};

class B : public A
{
public:
   B(int i) : A(i, 'B') { }

   static const char s_c = 'B';
};

class C : public A
{
public:
   C(int i) : A(i, 'C') { }

   static const char s_c = 'C';
};

template <class X>
class compareA
{
public:
   compareA() : m_i(999)
   {
   std::cout << "Construct functor " << X::s_c << m_i << std::endl;
   }

   compareA(const compareA &a) : m_i(a.m_i)
   {
   std::cout << "Copy functor " << X::s_c << m_i << std::endl;
   }

   ~compareA()
   {
   std::cout << "Destruct functor " << X::s_c << m_i << std::endl;
   }

   void operator= (const compareA &a)
   {
   m_i = a.m_i;
   std::cout << "Assign functor " << X::s_c << m_i << std::endl;
   }

   bool operator() (const X &x1, const X &x2) const
   {
   std::cout << "Comparing object " << x1.m_i << " with " << x2.m_i << std::endl;
   return x1.m_i < x2.m_i;
   }

private:
   int m_i;
};


typedef std::set<C, compareA<C> > SetTest;
typedef std::map<B, SetTest, compareA<B> >  MapTest;

int main()
   {
   int i = 0;
   std::cout << "--- " << i++ << std::endl;
   MapTest mapTest;
   std::cout << "--- " << i++ << std::endl;
   SetTest &setTest = mapTest[0];
   std::cout << "--- " << i++ << std::endl;
   }

如果我用 VS2005 编译此代码,我会得到以下输出:

--- 0
Construct functor B999
Copy functor B999
Copy functor B999
Destruct functor B999
Destruct functor B999
--- 1
Construct object B0
Construct functor C999
Copy functor C999
Copy functor C999
Destruct functor C999
Destruct functor C999
Copy object B0
Copy functor C999
Copy functor C999
Copy functor C999
Destruct functor C999
Destruct functor C999
Copy object B0
Copy functor C999
Copy functor C999
Copy functor C999
Destruct functor C999
Destruct functor C999
Destruct functor C999
Destruct object B0
Destruct functor C999
Destruct object B0
--- 2

如果我用 VS2010 编译此代码,我会得到以下输出:

--- 0
Construct functor B999
Copy functor B999
Copy functor B999
Destruct functor B999
Destruct functor B999
--- 1
Construct object B0
Construct functor C999
Copy functor C999
Copy functor C999
Destruct functor C999
Destruct functor C999
Copy object B0
Copy functor C999
Copy functor C999
Copy functor C999
Destruct functor C999
Destruct functor C999
Copy functor C999
Assign functor C999
Assign functor C999
Destruct functor C999
Copy object B0
Copy functor C999
Copy functor C999
Copy functor C999
Destruct functor C999
Destruct functor C999
Copy functor C999
Assign functor C999
Assign functor C999
Destruct functor C999
Destruct functor C999
Destruct object B0
Destruct functor C999
Destruct object B0
--- 2

第一个语句(构造映射)的输出是相同的。

第二条语句的输出(在映射中创建第一个元素并获取对其的引用)在 VS2010 情况下要大得多:

  • 函子的复制构造函数:10 次与 8 次
  • 函子的赋值:2 次与 0 times
  • 函子的析构函数:10 次 vs. 8 次

我的问题是:

  • 为什么 STL 复制函子?为集合的每个实例构造一次还不够吗?
  • 为什么 VS2010 情况下的函子构造比 VS2005 情况下更多? (没有检查VS2008)
  • 为什么在VS2010中分配了两次而不是在VS2005中分配了两次?
  • 有什么技巧可以避免仿函数的复制吗?

我在 防止不必要的 C++ 函子对象副本看到了类似的问题,但是我不确定这是同一个问题。

When moving from VS2005 to VS2010 we noticed a performance decrease, which seemed to be caused by additional copies of a functor.

The following code illustrates the problem. It is essential to have a map where the value itself is a set. On both the map and the set we defined a comparison functor (which is templated in the example).

#include <iostream>
#include <map>
#include <set>

class A
{
public:
   A(int i, char c) : m_i(i), m_c(c)
   {
   std::cout << "Construct object " << m_c << m_i << std::endl;
   }

   A(const A &a) : m_i(a.m_i), m_c(a.m_c)
   {
   std::cout << "Copy object " << m_c << m_i << std::endl;
   }

   ~A()
   {
   std::cout << "Destruct object " << m_c << m_i << std::endl;
   }

   void operator= (const A &a)
   {
   m_i = a.m_i;
   m_c = a.m_c;
   std::cout << "Assign object " << m_c << m_i << std::endl;
   }

   int m_i;
   char m_c;

};

class B : public A
{
public:
   B(int i) : A(i, 'B') { }

   static const char s_c = 'B';
};

class C : public A
{
public:
   C(int i) : A(i, 'C') { }

   static const char s_c = 'C';
};

template <class X>
class compareA
{
public:
   compareA() : m_i(999)
   {
   std::cout << "Construct functor " << X::s_c << m_i << std::endl;
   }

   compareA(const compareA &a) : m_i(a.m_i)
   {
   std::cout << "Copy functor " << X::s_c << m_i << std::endl;
   }

   ~compareA()
   {
   std::cout << "Destruct functor " << X::s_c << m_i << std::endl;
   }

   void operator= (const compareA &a)
   {
   m_i = a.m_i;
   std::cout << "Assign functor " << X::s_c << m_i << std::endl;
   }

   bool operator() (const X &x1, const X &x2) const
   {
   std::cout << "Comparing object " << x1.m_i << " with " << x2.m_i << std::endl;
   return x1.m_i < x2.m_i;
   }

private:
   int m_i;
};


typedef std::set<C, compareA<C> > SetTest;
typedef std::map<B, SetTest, compareA<B> >  MapTest;

int main()
   {
   int i = 0;
   std::cout << "--- " << i++ << std::endl;
   MapTest mapTest;
   std::cout << "--- " << i++ << std::endl;
   SetTest &setTest = mapTest[0];
   std::cout << "--- " << i++ << std::endl;
   }

If I compile this code with VS2005 I get the following output:

--- 0
Construct functor B999
Copy functor B999
Copy functor B999
Destruct functor B999
Destruct functor B999
--- 1
Construct object B0
Construct functor C999
Copy functor C999
Copy functor C999
Destruct functor C999
Destruct functor C999
Copy object B0
Copy functor C999
Copy functor C999
Copy functor C999
Destruct functor C999
Destruct functor C999
Copy object B0
Copy functor C999
Copy functor C999
Copy functor C999
Destruct functor C999
Destruct functor C999
Destruct functor C999
Destruct object B0
Destruct functor C999
Destruct object B0
--- 2

If I compile this with VS2010, I get the following output:

--- 0
Construct functor B999
Copy functor B999
Copy functor B999
Destruct functor B999
Destruct functor B999
--- 1
Construct object B0
Construct functor C999
Copy functor C999
Copy functor C999
Destruct functor C999
Destruct functor C999
Copy object B0
Copy functor C999
Copy functor C999
Copy functor C999
Destruct functor C999
Destruct functor C999
Copy functor C999
Assign functor C999
Assign functor C999
Destruct functor C999
Copy object B0
Copy functor C999
Copy functor C999
Copy functor C999
Destruct functor C999
Destruct functor C999
Copy functor C999
Assign functor C999
Assign functor C999
Destruct functor C999
Destruct functor C999
Destruct object B0
Destruct functor C999
Destruct object B0
--- 2

The output for the first statement (constructing the map) is identical.

The output for the second statement (creating the first element in the map and getting a reference to it), is much bigger in the VS2010 case:

  • Copy constructor of functor: 10 times vs. 8 times
  • Assignment of functor: 2 times vs. 0 times
  • Destructor of functor: 10 times vs. 8 times

My questions are:

  • Why does the STL copy a functor? Isn't it enough to construct it once for every instantiation of the set?
  • Why is the functor constructed more in the VS2010 case than in the VS2005 case? (didn't check VS2008)
  • And why is it assigned two times in VS2010 and not in VS2005?
  • Are there any tricks to avoid the copy of functors?

I saw a similar question at Prevent unnecessary copies of C++ functor objects but I'm not sure that's the same question.

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

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

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文