继承 noncopyable 对 dllexport 类没有影响

发布于 2024-12-05 06:10:11 字数 2294 浏览 3 评论 0原文

更新下面提到的错误在VS2012中已修复,并且noncopyable按预期工作

这既是一个问题,也是一种提供信息/警告其他人的方式,这样他们就不会陷入困境和我一样的陷阱:使用 MS 编译器时,使用不可复制基类(如 boost 中的基类)对导出类没有任何影响。这是微软的已知错误,但我怀疑是否有很多程序员知道它。可以想象,这可能会产生极其严重的错误,因为它允许编写甚至不应该编译的代码。示例(不可复制类的代码此处:)

典型的头文件在 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 );
}

其他情况: 无法复制我 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 放入不可复制的复制构造函数中也不会删除它,因为即使没有调用它,编译器也始终会对其进行编译。

UPDATE the belowmentioned bug is fixed in VS2012, and noncopyable works as epected

This is both a question and a way to provide information / warn others so they don't fall into the same trap as I did: it seems that using a noncopyable base class (like the one in boost) has no effect in exported classes when using the MS compiler. This is a known bug to MS but I doubt there are a lot of programmers that know of it. As one can imagine, this can produce extremely nasty bugs because it allows writing code that should not even compile. Example (code for noncopyable class here:)

a typical header file in a dll project, compile with /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();

the source file:

#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
}

the application:

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

the output:

constructor
destructor
destructor

1 constructor, 2 destructors called. Imagine the problems when the class effectively contains resources.

edit
usage cases that do compile but should not:

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

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

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

other cases:
CantCopyMe MakeIt()
{
return CantCopyMe(); //fatal error 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
}  

Questions:

  1. The KB article mentions a fix in an upcoming release. Can anyone check if this is fixed in VS2010 already (or possibly with a Visual Studio 11 preview)?

  2. Is there any workaround to trigger any kind of error? I tried (ab)using the fact that writing return CantCopyMe() triggers an internal compiler error but, I couldn't find a way to conditionally trigger it only when compiling a function like MakeIt above. Putting static_assert in the noncopyable's copy constructor also doesn't cut it since the compiler will always compile it even if it doesn't get invoked.

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

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

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

埋葬我深情 2024-12-12 06:10:11

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

To 2。我想一种方法是:

  • 不再从不可复制
  • 派生,在 CantCopyMe 中将复制构造函数和赋值运算符声明为私有,而不提供实现)
类 CantCopyMe 
{
民众:
   //为简洁起见省略...
私人的:
    CantCopyMe( const CantCopyMe& );
    const CantCopyMe&运算符=( const CantCopyMe& );
};

完成此操作后,您就避免了您所描述的危险情况,并且它也应该适用于 VS2008。并且您在正确的位置解决了问题,即在不可复制的类声明中。

To answer 1 (for VS2010), I just tried it in VS2010 (with SP1) and it compiles just fine, which means it has not been fixed. Unfortunately I do not have 2011 to test

To 2. I guess one way to do it would be to:

  • no longer derive from noncopyable
  • declare the copy ctor and assignment operator as private in CantCopyMe without providing an implementation)
class CantCopyMe 
{
public:
   //omitted for brevity...
private:
    CantCopyMe( const CantCopyMe& );
    const CantCopyMe& operator=( const CantCopyMe& );
};

With this done you avoided the dangerous situations you describe and it should work with VS2008 also. And you solve the problem in the right place, that is in the non-copyable class declaration.

方觉久 2024-12-12 06:10:11

我刚刚在稍微不同的情况下遇到了同样的问题:我有一个 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 编译它时,我得到:错误 C4716:'Outer::Copy':必须返回一个值。如果我将 Copy() 更改为:

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

我现在只收到一个奇怪的警告:警告 C4700:使用了未初始化的局部变量“复制”,并且在运行时发生了严重的崩溃。最后,尝试一下:

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

编译器肯定会崩溃!这是在 VS2010 SP1 上,80x86 的 C++ 编译器版本 16.00.40219.01。

I just ran into this same issue in a slightly different situation: I have a DLL exported class, which was given a member that is non-copyable. The DLL exported class has no explicit copy-constructor and has a Copy method that returns a copy of itself on the heap. When the non-copyable member was added, there was no compiler error but a nasty runtime error. I tracked it down to the __declspec(dllexport) and found if I removed it I got the expected and correct compiler error preventing the copy. Consider this minimal example:

#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);
    }
};

When I compile this with the latest VS2010 I get: error C4716: 'Outer::Copy' : must return a value. If I change Copy() to this:

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

I now get only an odd warning: warning C4700: uninitialized local variable 'copy' used, and a nasty crash at runtime. Finally, give this a try:

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

The compiler will reliably crash! This is on VS2010 SP1, C++ Compiler Version 16.00.40219.01 for 80x86.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文