包含auto_ptr的类存储在向量中

时间:2009-04-01 09:35:10

标签: c++ stl

在对Is it safe to store objects of a class which has an std::auto_ptr as its member variable in std::vector?的回答中,我声明包含auto_ptr的类可以存储在向量中,前提是该类具有用户定义的复制构造函数

有几条评论表明事实并非如此,所以这个问题试图解决这个问题。请考虑以下代码:

#include <memory>
#include <vector>
using namespace std;

struct Z {};

struct A {

    A( Z z ) 
        : p( new Z(z) ) {} 

    A( const A & a ) 
        : p( a.p.get() ? new Z( *a.p.get()) : 0 ) {}

    // no assigment op or dtor defined by intent

    auto_ptr <Z> p;
};

int main() {
    vector <A> av;              
    Z z;                    
    A a(z);
    av.push_back( a );      
    av.push_back( A(z) );   
    av.clear();             
}                           

请检查上述&amp;在你的回复中指出未定义的位置 对于以这种特定方式使用的特定类,可能会出现C ++标准含义中的行为。我不感兴趣该类是否有用,表现良好,可排序,或者它是如何在异常情况下执行的。

请注意,这不是关于创建auto_ptrs向量的有效性的问题 - 我很清楚相关问题。

  

感谢所有人对你的内容的投入   回想起来可能是相当愚蠢的   题。我想我太专注了   在副本ctor&amp;忘记了   分配。我的幸运赢家   接受点(和点意味着   奖品!)通常是 litb   详尽的解释(对不起   埃里克)

5 个答案:

答案 0 :(得分:4)

存储在容器中的对象必须是“CopyConstructable”以及“Assignable”(C ++ 2008 23.1 / 3)。

你的类试图处理CopyConstructable的要求(虽然我认为它仍然不符合它 - 我编辑了这个论点,因为它不是必需的,因为我认为这是有争议的),但它没有处理可分配的要求。要可分配(C ++ 2008 23.1 / 4),必须满足以下条件:t的值为Tu的值为(constT

  

t = u返回T&t相当于u

该标准还在注释(20.4.5 / 3)中说:“auto_ptr不符合标准库容器元素的CopyConstructible和Assignable要求,因此实例化标准库容器{{1}导致未定义的行为。“

由于您没有声明或定义赋值运算符,因此将提供使用auto_ptr赋值运算符的隐式赋值运算符,这肯定会使auto_ptr不等同于t更不用说它根本不适用于“u”值(这是Earwicker's answer指出的 - 我只是指出了标准的确切部分)。

答案 1 :(得分:3)

我认为上述代码甚至不会编译。当然std::vector的实施者可以自由地要求提供一个赋值算子,来自const A&

刚刚尝试过,它无法在Visual Studio C ++ 2008 Service Pack 1上编译:

  

binary'=':没有运算符找到哪个   采用类型的右手操作数   'const A'(或者没有可接受的   转化率)

我的猜测是,在Herb Sutter的指导下,VC ++中的容器类会尽一切努力对其类型参数强加标准要求,特别是使auto_ptr很难使用它们。他们可能已经超越了标准设定的界限,但我似乎记得它强制要求真正的作业以及真正的复制结构。

然而,它在g ++ 3.4.5中编译。

答案 2 :(得分:3)

尝试将地点列表放在一起,使示例未定义行为。

#include <memory>
#include <vector>
using namespace std;

struct Z {};

struct A {

    A( Z z ) 
        : p( new Z(z) ) {} 

    A( const A & a ) 
        : p( a.p.get() ? new Z( *a.p.get()) : 0 ) {}

    // no assigment op or dtor defined by intent

    auto_ptr <Z> p;
};

int main() {
    vector <A> av;  
    ...
}

我将检查您使用类型A实例化向量的行。标准必须说

23.1/3

  

存储在这些组件中的对象类型必须满足CopyConstructible类型(20.1.3)的要求,以及可分配类型的附加要求。

23.1/4(强调我的):

  

在表64中,T是用于实例化容器的类型,t是T的值,u是(可能是const )T的值。

+-----------+---------------+---------------------+
|expression |return type    |postcondition        |
+-----------+---------------+---------------------+
|t = u      |T&             |t is equivalent to u |
+-----------+---------------+---------------------+
     

表64

12.8/10

  

如果类定义没有显式声明一个复制赋值运算符,则会隐式声明一个。类X的隐式声明的复制赋值运算符将具有

形式
X& X::operator=(const X&)
     

如果

     
      
  • X的每个直接基类B都有一个复制赋值运算符,其参数类型为const B&amp;,   const volatile B&amp;或B,和
  •   
  • 对于类型为M(或其数组)的X的所有非静态数据成员,每个这样的类类型具有复制赋值运算符,其参数是const M&amp;,const volatile M&amp;或者M。
  •   
     

否则,隐式声明的复制赋值运算符将具有

形式
X& X::operator=(X&)

(注意最后一句和后一句)

17.4.3.6/1 and /2

  

在某些情况下(替换函数,处理函数,用于实例化标准库模板组件的类型的操作),C ++标准库依赖于C ++程序提供的组件。如果这些组件不符合要求,则标准对实施没有要求。

     

特别是,在以下情况下效果未定义:

     
      
  • 用于实例化模板组件时用作模板参数的类型,如果该类型的操作未实现适用的Requirements子句的语义(20.1.5,23.1,24.1,26.1)。除非另有说明,否则此类类型的操作可以通过抛出异常来报告失败。
  •   

现在,如果你看一下auto_ptr的规范,你会注意到它有一个带有非const auto_ptr的复制赋值运算符。因此,类的隐式声明的复制赋值运算符将非const类型作为其参数。如果您仔细阅读上述地方,您将看到如何实例化您所写类型的向量是未定义的行为。

答案 3 :(得分:0)

由于常规auto_ptr语义可能表明在复制过程中传递了所有权,因此我宁愿在此使用boost::scoped_ptr。当然缺少赋值运算符。

答案 4 :(得分:0)

以下情况如何?

cout << av[ 0 ] << endl;

另外,从概念上讲,副本应该保持复制的项目不变。在您的实施中违反了这一点。

(你的原始代码与g++ -pedantic ...和Comeau编译完全相反,而不是VS2005。)