为什么以下代码在VS2010中成功编译?

时间:2011-04-30 08:55:05

标签: c++

我有以下异常类。

class ExceptionTest : std::exception
{
public:
ExceptionTest(int value):
  m_value(value)
{
}
~ExceptionTest()
{
}
private:
ExceptionTest(const ExceptionTest& test)
{
}

int m_value;
};

然后我以这种方式使用它 -

int checkexception()
{
throw ExceptionTest(2);
}

int main()
{
try
{
    checkexception();
}
catch (ExceptionTest& exception)
{
    cout<<"haha";
}
return 1;
}

即使复制构造函数是私有的,这也完全正常。

如果按值捕获异常,则失败 -

int main()
{
try
{
    checkexception();
}
catch (ExceptionTest exception) --> this fails
{
    cout<<"haha";
}
return 1;
}

我得到的错误是

error C2316: 'ExceptionTest' : cannot be caught as the destructor and/or copy   
constructor are inaccessible

如果我没有在类

中定义复制构造函数,我会收到链接器错误
class ExceptionTest : std::exception
{
public:
ExceptionTest(int value):
  m_value(value)
{
}
~ExceptionTest()
{
}
private:
ExceptionTest(const ExceptionTest& test);

int m_value;
};

LINK:C:\ Users \ sumitha \ Documents \ Visual Studio 2010 \ Projects \ test \ Debug \ test.exe未找到或未由最后一个增量链接构建;执行完整链接 1&gt; main.obj:错误LNK2001:未解析的外部符号“private:__thiscall ExceptionTest :: ExceptionTest(class ExceptionTest const&amp;)”(?? 0ExceptionTest @@ AAE @ ABV0 @@ Z) 1&gt; C:\ Users \ sumitha \ Documents \ Visual Studio 2010 \ Projects \ test \ Debug \ test.exe:致命错误LNK1120:1个未解析的外部 ==========构建:0成功,1个失败,0个最新,0个跳过==========

如果上述情况属实,我们总是可以将异常类的复制构造函数设为private,这样调用者就会被强制通过引用捕获异常。我认为这是因为“返回值优化”

4 个答案:

答案 0 :(得分:6)

这似乎是VS2010的C ++实现中的一个错误。

throw表达式时,通过复制(或移动)throw表达式的操作数来创建临时异常对象。如果表达式具有类类型,则涉及复制(或移动)构造函数,并且构造函数必须在throw处可访问。如果异常对象的拷贝构造函数是私有的,那么该对象只能从成员函数或友元函数抛出。

此问题完全取决于异常对象是否随后被值捕获或稍后在程序中引用。

在构造临时异常对象时,可能会省略实际副本,但C ++要求仍然可以访问已使用的构造函数。

ISO / IEC 14882:2003 15.1 [except.throw] / 5:

  

如果可以在不改变程序含义的情况下消除临时对象的使用,除了执行与使用临时对象(12.2)相关的构造函数和析构函数,那么处理程序中的异常可以直接初始化使用throw表达式的参数。当抛出的对象是类对象,并且无法访问用于初始化临时副本的复制构造函数时,程序格式错误(即使临时对象可能被消除)。   同样,如果该对象的析构函数不可访问,则程序格式错误(即使临时对象可能被删除)。

此要求尚未在C ++ 0x中删除,尽管现在可以移动throw表达式而不是在适当的位置复制。

草案n3291 15.1 [except.throw] / 5:

  

当抛出的对象是类对象时,即使复制/移动操作被省略,也应该可以访问复制/移动构造函数和析构函数(12.8)。

答案 1 :(得分:1)

像马里诺说的那样,如果你通过引用捕获,就不会涉及复制结构。

因此,编译器不会查找它,并且永远不会在私有构造函数上失败:)

作为旁注,我记得特别针对异常,在规范中有关于传值语义的“异常”(没有双关语意)。我不知道具体细节,但它与

有关
  • 从自动变量(stackallocated)传递本地异常,这在解除堆栈时自然有点棘手
  • 可能特别是在重新抛出由值
  • 捕获的异常的情况下

....
catch (std::runtime_error e)
{
     // ....
     throw;
}

答案 2 :(得分:0)

当您使用引用(&amp; symbol)捕获异常时,您将获得生成的相同异常对象,即catch块中的变量将指向被抛出的内存中的同一异常对象,因此不需要复制缺点。如果您使用另一个按值捕获异常的catch块,那么编译器需要生成代码以创建正被捕获的异常对象的副本,并将此新副本分配给catch块中的变量,因此你需要有公共副本。

答案 3 :(得分:0)

将复制构造函数(和赋值运算符)设为私有是C ++中一种非常广泛使用的技术,用于防止复制。可以说,绝大多数C ++类(但不是异常,必须具有可访问的拷贝构造函数)应该禁用复制 - 我知道我自己的代码中的几乎所有类都这样做。