Ответ 1
Это не столько ответ, сколько больше наблюдений об этой ситуации. Это действительно интригующий вопрос, потому что действительно кажется что-то (я не знаю, что), что делает конструкторы (и деструкторы) принципиально несовместимыми с вызывающими соглашениями.
Прежде всего, не ищите ответа в стандарте С++, потому что стандарт не заботится о вызовах конвенций, на самом деле, единственное упоминание об этом в стандарте - это пример того, что выходит из объем этого документа (т.е. "определенный реализацией" ). Таким образом, стандарт не запрещает возможность указания или использования разных соглашений вызова для конструкторов (или деструкторов).
Из небольшого исследования и тестирования, похоже, ситуация для разных основных компиляторов (насколько я мог бы собрать):
- MSVC позволяет вам указывать соглашение о вызове, но игнорирует его (с предупреждением) для чего-либо иного, кроме "stdcall", который он считает как "thiscall" (stdcall-style thiscall), который по-прежнему является значением по умолчанию. Изменение соглашения о вызове по умолчанию с помощью параметра командной строки (например,
/Gz
) не влияет на нестатические функции-члены, такие как конструкторы. Другими словами, нет способа изменить вызывающее соглашение на конструкторы, независимо от того, что вы пытаетесь указать, оно будет проигнорировано. - Как ICC, так и GCC игнорируют (с предупреждением) любую попытку указать соглашение о вызове конструктора.
- Clang игнорирует (с предупреждением) любую попытку указать соглашение о вызове в конструкторе, если только это не "cdecl", который также является стандартным (cdecl-style thiscall).
Другими словами, все компиляторы кажутся очень упрямыми, поскольку не допускают каких-либо других соглашений о вызове конструкторов, кроме стандартного. И главный вопрос: почему?
Вот несколько мыслей об этом (спекуляции).
Во-первых, традиционно соглашения о вызовах были в большей степени совместимы с бинарностью, чем с оптимизацией производительности, то есть вы указываете соглашения о вызовах при связывании с вызовами и вызовами с разных языков или компиляторов (например, традиционно между C, С++, Pascal и Fortran). Таким образом, это может объяснить, почему некоторые функции С++ исключены из этого. Например, если вы создаете интерфейс для библиотеки С++ (возможно, называете его на другом языке), то вам нужно либо отказаться от C API, то есть не иметь классов, нет конструкторов и не использовать функции-члены, или вы выставить С++ API на платформе со стандартным ABI (например, Itanium), который исправляет соглашение о вызове. Это делает указание условного вызова для конструкторов совершенно бесполезным с точки зрения совместимости двоичных файлов. Это может объяснить, почему это было "проигнорировано" (не реализовано компиляторами) как низкоприоритетная функция.
Еще одна проблема, которая может быть задействована здесь для конструкторов, заключается в том, что если вы посмотрите пример в спецификации Itanium ABI, вы можете увидеть, что вызовы конструктора довольно особый. В частности, для виртуального наследования требуется передать временный указатель виртуальной таблицы. Это может объяснить, почему разработчикам компилятора довольно сложно предоставить вариант каждого возможного соглашения о вызове для конструкторов. Другими словами, это не так просто, как просто добавить указатель "this" в качестве первого "скрытого" аргумента, а затем применить стандартное соглашение о вызове C (как и для обычных нестатических функций-членов). Я не думаю, что было бы невозможно реализовать эти альтернативные соглашения о вызовах для конструкторов, но было бы лишней проблемой, что компиляторы-поставщики просто не смогли реализовать (пока). Кроме того, учитывая вызовы конструкторов базового класса, он также может создавать проблемы с диагностикой (проверка того, что соглашения о вызове конструктора базового класса совместимы с конвенцией о вызове производного класса). Таким образом, это может быть просто "слишком много неприятностей, не делал этого". И тот факт, что стандарт позволяет это, но ни один из компиляторов не делает этого, является сильным индикатором того, что это может быть так.
Помимо этого, я не могу дать окончательного ответа относительно того, почему этого было бы невозможно достичь. Единственный способ получить окончательный ответ - найти человека, который непосредственно вовлечен в реализацию одного из этих компиляторов, и который попытался поддержать различные соглашения о вызовах для конструкторов. И обратите внимание, что возможно, что такой человек вообще не существует (лучше всего проверить, чтобы сообщество разработчиков LLVM/Clang обнаружило, что кто-то заглянул в эту проблему).