是否有按值传递类对象的标准过程?

时间:2014-03-27 00:39:54

标签: c++ winapi c++11 standards

是否有按值传递类的标准过程?换句话说,如果我这样做:

struct Test
{
  int a;
  double b;
}

void DoSomething(Test t)
{
  std::cout << t.a << std::endl;
  std::cout << t.b << std::endl;
}

//...

Test myObj;
myObj.a = 5;
myObj.b = 10.5;

DoSomething(myObj);

假设标准打包和布局,标准是否提供任何保证,无论编译器如何,都将以一致的方式发送和接收类?


因为我预计会出现类似问题的问题,为什么要这样做?&#34;或者&#34;这感觉就像一个XY问题&#34;,here's (lengthy) context。我试图在EXE和用不同编译器编译的DLL之间来回传递类对象,看起来没有从正确的起始地址读取类对象。但是,如果我通过引用传递对象,这个问题就会消失。 (额外的问题 - 为什么通过参考工作时通过值不会?我在传递值的印象下会复制对象并传递对副本的引用。显然我在这里误解了一些东西。)

1 个答案:

答案 0 :(得分:5)

通常,不同C ++编译器之间的ABI可以以他们认为合适的任何方式变化。 C ++标准没有强制要求给定的ABI。

然而,C ABI非常稳定。解决此问题的一种方法是使用仅限标头的函数将代码转换为extern "C"函数,这些函数从DLL导出。在DLL中,extern "C"函数调用更传统的C ++接口。

具体例子,

struct Test;
// DLL exported:
extern "C" void Private_DoSomething_Exported( Test* );

// Interface:
namespace Interface {
  inline void DoSomething( Test t ) { return Private_DoSomething_Exported(&t); }
};

// implementation.  Using Test&& to make it clear that the reference is not an export parameter:
namespace Implementation {
  void DoSomething( Test&&t ) {
    std::cout << t.a << std::endl;
    std::cout << t.b << std::endl;
  }
}
void Private_DoSomething_Exported( Test* t ) {
  Assert(t);
  Implementation::DoSomething(std::move(*t));
}

这使得&#34;最兼容&#34; ABI(一个纯粹的&#34; C&#34; ABI),从DLL导出函数。客户端代码调用Interface::DoSomething,客户端代码中的inline调用"C" ABI(它甚至不知道对象的布局),然后调用C ++ { {1}}。

这仍然不能证明每个问题,因为甚至POD结构的布局可能因编译器而异(作为一个实际示例,一些编译器将Implementation::DoSomething视为64位计算机上的32位,其他编译器处理{{ 1}}在64位机器上为64位)。包装也可能有所不同。

为了减少这种影响,您首先要仅使用C头文件中的固定大小类型。您还要检查两个编译器的打包文档。