在对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 详尽的解释(对不起 埃里克)
答案 0 :(得分:4)
存储在容器中的对象必须是“CopyConstructable”以及“Assignable”(C ++ 2008 23.1 / 3)。
你的类试图处理CopyConstructable的要求(虽然我认为它仍然不符合它 - 我编辑了这个论点,因为它不是必需的,因为我认为这是有争议的),但它没有处理可分配的要求。要可分配(C ++ 2008 23.1 / 4),必须满足以下条件:t
的值为T
而u
的值为(const
)T
:
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。)