gcc 对象按值或按地址传递

gcc object passing by value or by address

我们有这个简单的 class:

class MyStr
{
public:
    MyStr() { }
    wchar_t* pStr;
};

以及使用它的代码:

MyStr ms1, ms2;
ms1.pStr = L"MyStr!"; 
ms2.pStr = L"MyStr2!";

wchar_t buffer[100];
swprintf(buffer, 100, L"%ls - %ls", ms1, ms2);

将 ms1 和 ms2 压入堆栈的程序集如下所示:

FF B5 24 FE FF FF    push DWORD PTR [ebp-0x1dc] 
FF B5 20 FE FF FF    push DWORD PTR [ebp-0x1e0] 

它实际上是将 MyStr(在本例中只是 pStr)的 values/content 压入堆栈。

如果我们将 MyStr 更改为只添加一个简单的析构函数:

class MyStr
{
public:
    MyStr() { }
    ~MyStr() { }
    wchar_t* pStr;
};

现在传递的是 ms1 和 ms2 的地址,而不是它们的 values/content。

8D 85 24 FE FF FF    lea eax,[ebp-0x1dc] 
50                   push eax 
8D 85 20 FE FF FF    lea eax,[ebp-0x1e0] 
50                   push eax

Visual Studio on Windows 两种方式给出相同的结果(总是通过 values/contents),但是 gcc on Linux 给出这两个不同的结果。

  1. 为什么?
  2. 我们可以做些什么来保留析构函数但按值而不是 reference/address 传递?

我们不能做的是更改 swprintf 行 - 有成千上万行,我们正在努力避免更改它们。

你可以求助于 MACRO

// Forward call to your method
#define swprintf(...) my_swprintf(__VA_ARGS__)

// Your class
class MyStr
{
public:
    MyStr() { }
    ~MyStr() { }
    const wchar_t* pStr;
};


// helper function to "fix" parameters
template <typename T>
std::enable_if_t<std::is_arithmetic<T>::value || std::is_pointer<T>::value, T>
normalize(T t) { return t; } // Default one which does nothing

// Fix for your class to return its member.
const wchar_t* normalize(const MyStr& t) { return t.pStr; }

// Your function which (re-)forward to real swprintf with fixed parameter
// Extra parent do avoid macro substitution
template <typename ... Ts>
auto my_swprintf(wchar_t* buffer, const wchar_t* format, const Ts&... args)
{
    return (swprintf)(buffer, format, normalize(args)...);
}