【问题标题】:inheriting noncopyable has no effect in dllexport classes继承 noncopyable 对 dllexport 类没有影响
【发布时间】:2011-11-20 22:12:32
【问题描述】:

更新 VS2012 修复了下面提到的错误,noncopyable 工作正常

这既是一个问题,也是一种提供信息/警告他人的方式,这样他们就不会像我一样落入同样的陷阱:似乎使用 noncopyable 基类(就像 boost 中的那个)在使用 MS 编译器时对导出的类没有影响。这是 a known bug 给 MS 但我怀疑有很多程序员知道它。可以想象,这会产生非常讨厌的错误,因为它允许编写甚至不应该编译的代码。示例(不可复制类的代码here:)

dll项目中典型的头文件,用/D EXPORT_IT编译:

#ifdef EXPORT_IT
  #define mydll __declspec( dllexport )
#else
  #define mydll __declspec( dllimport )
#endif    

class mydll CantCopyMe : private noncopyable
{
public:
  CantCopyMe();
  ~CantCopyMe();
};

mydll CantCopyMe MakeIt();

源文件:

#include <iostream>

CantCopyMe::CantCopyMe()
{
  std::cout << "constructor" << std::endl;
}

CantCopyMe::~CantCopyMe()
{
  std::cout << "destructor" << std::endl;
}

CantCopyMe MakeIt()
{
  CantCopyMe x;
  return x; //oops... this sould not compile nor link but it does
}

应用程序:

int main()
{
  CantCopyMe x( MakeIt() );
}

输出:

constructor
destructor
destructor

1 个构造函数,2 个析构函数被调用。想象一下当类有效地包含资源时会出现什么问题。

编辑 可以编译但不应该编译的用例:

CantCopyMe MakeIt()
{
  CantCopyMe x;
  return x;
}

void DoIt( CantCopyMe x )
{
  x.Foo();
}

void SomeFun()
{
  CantCopyMe x;
  DoIt( x );
}

其他情况: CantCopyMe MakeIt() { 返回 CantCopyMe(); //致命错误C1001 }

CantCopyMe GenerateIt()
{
  CantCopyMe x;
  return x;
}

CantCopyMe MakeIt()
{
  return GenerateIt(); //fatal error C1001
}

CantCopyMe MakeIt()
{
  CantCopyMe x;
  return CantCopyMe( x ); //fatal error C1001 + cl crashes
}

void DoSomething()
{
  CantCopyMe x;
  CantCopyMe y = x; //fatal error C1001 + cl crashes
}  

问题

  1. 知识库文章提到了即将发布的版本中的修复。任何人都可以检查这是否已在 VS2010 中修复(或者可能使用 Visual Studio 11 预览版)?

  2. 是否有任何解决方法可以触发任何类型的错误?我尝试(ab)使用编写return CantCopyMe() 触发内部编译器错误的事实,但是,我无法找到一种方法来有条件地触发它,只有在编译像上面的MakeIt 这样的函数时才会有条件地触发它。将 static_assert 放在不可复制的复制构造函数中也不会削减它,因为即使它没有被调用,编译器也会始终编译它。

【问题讨论】:

  • 这种情况是仅返回一个对象还是您尝试过其他上下文?也许您的编译器在应用 NRVO 时没有检查可访问的复制构造函数(这是不合格的)。
  • @Luc Danton 将编辑并添加一些其他案例,不确定这是否是您对“其他上下文”的意思;确实可能是这里应用的一种相当残缺的 NRVO 形式(没有调用复制构造函数,但有析构函数)..

标签: c++ visual-studio visual-c++ dll noncopyable


【解决方案1】:

回答 1(对于 VS2010),我刚刚在 VS2010(带有 SP1)中尝试过,它编译得很好,这意味着它还没有被修复。不幸的是,我没有 2011 年要测试

到 2。我想一种方法是:

  • 不再派生自不可复制
  • 在 CantCopyMe 中将复制 ctor 和赋值运算符声明为私有而不提供实现)
class CantCopyMe 
{
public:
   //omitted for brevity...
private:
    CantCopyMe( const CantCopyMe& );
    const CantCopyMe& operator=( const CantCopyMe& );
};

完成后,您避免了您描述的危险情况,它也应该适用于 VS2008。而且你在正确的地方解决了问题,那就是在不可复制的类声明中。

【讨论】:

  • 谢谢。在 CantCopyMe 中将复制 ctor 和赋值运算符声明为私有是我之前所做的(通过宏),但感觉就像违反了 DRY。我更喜欢继承不可复制,因为这是一个明确的声明..
  • 你好 stjin。是的,你可能是对的,它感觉不对,如果它能正常工作那就太好了。但鉴于问题存在这一事实,不幸的是,我没有看到其他更好的解决方案。我猜你仍然可以从 noncopyable 派生,也可以像以前一样使用宏来声明 CantCopyMe 的复制 ctor 和赋值运算符私有。在修复错误的某个时间点,您可以简单地将宏重新定义为空(或者简单地删除宏并查看错误出现在哪里,然后在使用它的任何地方将其删除)。
【解决方案2】:

我只是在稍微不同的情况下遇到了同样的问题:我有一个 DLL 导出类,它被赋予了一个不可复制的成员。 DLL 导出的类没有显式的复制构造函数,并且有一个 Copy 方法,该方法在堆上返回其自身的副本。添加不可复制成员时,没有编译器错误,但出现了令人讨厌的运行时错误。我将它追踪到 __declspec(dllexport) 并发现如果我删除它,我得到了预期的正确编译器错误,阻止了复制。考虑这个最小的例子:

#define API __declspec(dllexport)

class Inner
{
public:
    Inner() {}

private:
    Inner(const Inner&) {}
    Inner& operator=(const Inner&) { return *this; }
};

class API Outer
{
private:
    Inner i;

public:
    virtual Outer* Copy()
    {
        return new Outer(*this);
    }
};

当我用最新的 VS2010 编译它时,我得到:error C4716: 'Outer::Copy' : must return a value。如果我将 Copy() 更改为:

virtual Outer* Copy()
{
    Outer* copy = new Outer(*this);
    return copy;
}

我现在只收到一个奇怪的警告:warning C4700: uninitialized local variable 'copy' used,并在运行时出现严重的崩溃。最后,试一试:

virtual Outer* Copy()
{
    Outer tmp(*this);
    return nullptr;
}

编译器将可靠地崩溃!这是在 VS2010 SP1 上,C++ 编译器版本 16.00.40219.01 用于 80x86。

【讨论】:

    猜你喜欢
    • 2012-07-25
    • 1970-01-01
    • 2011-06-17
    • 1970-01-01
    • 2011-02-24
    • 2020-04-29
    • 2016-02-09
    • 2010-12-29
    • 1970-01-01
    相关资源
    最近更新 更多