C++ 在 dll 接口中使用 stl 类时消除 C4251 警告的一种方法

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/16419318/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 20:19:58  来源:igfitidea点击:

One way of eliminating C4251 warning when using stl-classes in the dll-interface

c++visual-studio-2010

提问by feelfree

It is not a good practice using stl-classes in the dll-interface as Common practice in dealing with warning c4251: class … needs to have dll-interfaceexplains. An example is given:

在 dll-interface 中使用 stl-classes 作为处理警告 c4251 的常见做法不是一个好习惯:class ...需要有 dll-interface解释。给出了一个例子:

#include <iostream>
#include <string>
#include <vector>


class __declspec(dllexport) HelloWorld
{
public:
    HelloWorld()
    {
        abc.resize(5);
        for(int i=0; i<5; i++)
            abc[i] = i*10;

        str="hello the world";
    }
    ~HelloWorld()
    {

    }

    std::vector<int> abc;
    std::string str;

};

When compiling this file, the following warnings can be observed:

编译此文件时,可以观察到以下警告:

 warning C4251: 'HelloWorld::str' : class 'std::basic_string<_Elem,_Traits,_Ax>' needs to have dll-interface to be used by clients of class 'HelloWorld'    
 warning C4251: 'HelloWorld::abc' : class 'std::vector<_Ty>' needs to have dll-interface to be used by clients of class 'HelloWorld'

Then the question is how we can implement the same functionality without using STL class vector and string. One implementation I could think of is as follows:

那么问题是我们如何在不使用 STL 类向量和字符串的情况下实现相同的功能。我能想到的一种实现如下:

class __declspec(dllexport) HelloWorld2
{
public:
    HelloWorld2()
    {
         abc_len = 5;
         p_abc = new int [abc_len];
         for(int i=0; i<abc_len; i++)
             p_abc[i] = i*10;

         std::string temp_str("hello_the_world");
         str_len = temp_str.size();
         p_str = new char[str_len+1];
         strcpy(p_str,temp_str.c_str());
    }
    ~HelloWorld2()
    {
        delete []p_abc;
        delete []p_str;
    }

    int *p_abc;
    int abc_len;
    char *p_str;
    int str_len;



};

As you can see, in the new implementation we use int *p_abc to substitute vector abc and char *p_str to replace string str. The question I have is whether there are other elegant implementation approaches that can do the same. Thanks!

如您所见,在新的实现中,我们使用 int *p_abc 替换向量 abc 和 char *p_str 替换字符串 str。我的问题是是否有其他优雅的实现方法可以做到这一点。谢谢!

回答by buygrush

I think you've got at least 5 possible options to solve this problem:

我认为您至少有 5 种可能的选择来解决这个问题:

  1. You could still keep STL/template classes for your fields and also use them as parameter types, as long as you keep all the fields private (which is a good practice anyway). Hereis a discussion about this. To remove the warnings you could simply use according #pragmastatements.

  2. You could create small wrapper classes with private STL fields and expose fields of your wrapper class types to the public instead, but this would most probably only move the warnings to another places.

  3. You could use the PIMPLidiom to hide your STL fields in a private implementation class, which will not even be exported from your library nor be visible outside of it.

  4. Or you could actually export all the required template class specializations, as suggested in the C4251 warning, in a way that is described here. In short you would have to insert following lines of code before your HelloWorldclass:

    ...
    #include <vector>
    
    template class __declspec(dllexport) std::allocator<int>;
    template class __declspec(dllexport) std::vector<int>;
    template class __declspec(dllexport) std::string;
    
    class __declspec(dllexport) HelloWorld
    ...
    

    By the way, the order of those exports seems to be important: the vector class template uses the allocator class template internally, so the allocator instantiation has to be exported beforethe vector instantiation.

  5. The direct use of intrinsics is probably your worst option, but if you encapsulate your fields

    int *p_abc;
    int abc_len;
    

    in something like class MyFancyDataArrayand the fields

    char *p_str;
    int str_len;
    

    in something like class MyFancyString, then this would be a more decent solution (similar to the one described at the second point). But it is probably not the best habit to reinvent the wheel too often.

  1. 您仍然可以为您的字段保留 STL/模板类,也可以将它们用作参数类型,只要您将所有字段保留为私有(无论如何这是一个很好的做法)。这里有一个关于这个的讨论。要删除警告,您可以简单地使用根据#pragma声明。

  2. 您可以创建带有私有 STL 字段的小型包装类,并将包装类类型的字段公开给公众,但这很可能只会将警告移动到其他地方。

  3. 您可以使用PIMPL习惯用法将您的 STL 字段隐藏在私有实现类中,该类甚至不会从您的库中导出,也不会在库外可见。

  4. 或者,您可以按照此处描述的方式,按照 C4251 警告中的建议,实际导出所有必需的模板类特化。简而言之,您必须在HelloWorld类之前插入以下代码行:

    ...
    #include <vector>
    
    template class __declspec(dllexport) std::allocator<int>;
    template class __declspec(dllexport) std::vector<int>;
    template class __declspec(dllexport) std::string;
    
    class __declspec(dllexport) HelloWorld
    ...
    

    顺便说一下,这些导出的顺序似乎很重要:向量类模板在内部使用分配器类模板,因此分配器实例化必须在向量实例化之前导出。

  5. 直接使用内部函数可能是你最糟糕的选择,但如果你封装你的字段

    int *p_abc;
    int abc_len;
    

    在类似class MyFancyDataArray和领域

    char *p_str;
    int str_len;
    

    在类似class MyFancyString,那么这将是一个更体面的解决方案(类似于第二点描述的解决方案)。但过于频繁地重新发明轮子可能不是最好的习惯。

回答by Rodrigo Hernandez

OR do the easiest thing to do, move the __declspecto the ONLY members you care to export:

或者做最简单的事情,将 移动__declspec到您关心的唯一成员导出:

class HelloWorld
{
public:
    __declspec(dllexport) HelloWorld()
    {
        abc.resize(5);
        for(int i=0; i<5; i++)
            abc[i] = i*10;

        str="hello the world";
    }
    __declspec(dllexport) ~HelloWorld()
    {
    }
    std::vector<int> abc;
    std::string str;
};

回答by Paul Groke

I'm not sure which problem you want to solve here. There are two different issues: Cross compiler binary compatibility and avoiding "undefined symbol" linker errors. The C4251 warning you quoted talks about the second issue. Which is really mostly a non-issue, especially with SCL classes like std::stringand std::vector. Since they're implemented in the CRT, as long as both sides of your application use the same CRT, everything will "just work". The solution in that case is to just disable the warning.

我不确定你想在这里解决哪个问题。有两个不同的问题:交叉编译器二进制兼容性和避免“未定义符号”链接器错误。您引用的 C4251 警告谈到了第二个问题。这实际上基本上不是问题,尤其是对于像std::string和 之类的 SCL 类std::vector。由于它们是在 CRT 中实现的,只要您的应用程序的双方都使用相同的 CRT,一切都会“正常工作”。在这种情况下的解决方案是禁用警告。

Cross compiler binary compatibility OTOH, which is what is discussed in the other stackoverflow question you linked, is a whole different beast. For that to work out, you basically have to keep all your public header files free of any mention of any SCL class. Which means you either have to PIMPL everything or use abstract classes everywhere (or a mix thereof).

交叉编译器二进制兼容性 OTOH,这是您链接的另一个 stackoverflow 问题中讨论的内容,是一个完全不同的野兽。为了解决这个问题,您基本上必须保持所有公共头文件都没有提及任何 SCL 类。这意味着您要么必须对所有内容进行 PIMPL,要么在任何地方使用抽象类(或混合使用)。

回答by qehgt

Just use Pointer To Implementation (pImpl)idiom.

只需使用指针到实现(pImpl)习惯用法。