C++ поиск безусловного имени: разный размер структуры в разных cpp приводит к тому, что оператор new выделяет меньше памяти, чем процессы конструктора?
Вот пример:
Main.cpp
:
#include "MooFoobar.h"
#include "MooTestFoobar.h"
#include "FoobarUser.h"
namespace moo::test::xxx {
struct X
{
void* operator new(const size_t size);
FoobarUser m_User;
};
void* X::operator new(const size_t size)
{
printf("Allocated size: %zd\n", size);
return malloc(size);
}
} // namespace moo::test::xxx
int main()
{
new moo::test::xxx::X;
printf("Actual size: %zd, member size: %zd\n", sizeof(moo::test::xxx::X), sizeof(moo::test::xxx::FoobarUser));
return 0;
}
MooFoobar.h
:
namespace moo {
struct Foobar
{
char m_Foo[64];
};
} // namespace moo
MooTestFoobar.h
:
namespace moo::test {
struct Foobar
{
char m_Foo[32];
};
} // namespace moo::test
FoobarUser.h
:
#include "MooFoobar.h"
namespace moo::test::xxx {
struct FoobarUser
{
FoobarUser();
~FoobarUser();
Foobar m_Foobar;
};
} // namespace moo::test::xxx
FoobarUser.cpp
:
#include "FoobarUser.h"
#include <cstdio>
moo::test::xxx::FoobarUser::FoobarUser()
: m_Foobar()
{
printf("FoobarUser constructor, size: %zd\n", sizeof(*this));
}
moo::test::xxx::FoobarUser::~FoobarUser()
{}
Итак, что здесь происходит: в зависимости от порядка включений неквалифицированное имя разрешается в разных типах и в FoobarUser.cpp
мы получаем размер 64
, в Main.cpp
мы получаем размер 32
, Не только sizeof
это отличается - operator new
вызывается с неверным (32
) размер, но конструктор будет инициализировать размер 64
те, которые приводят к повреждению памяти.
И в clang, и в msvc результат этой программы:
Allocated size: 32
FoobarUser constructor, size: 64
Actual size: 32, member size: 32
Это звучит очень подозрительно и в основном означает, что неквалифицированные имена не допускаются, если есть конфликт имен, потому что в зависимости от порядка включения это может привести к тому, что по сути является неправильной программой.
Но я не могу найти ни одного пункта в стандарте C++, который мог бы сказать что-либо из этого неверного / неправильно сформированного кода. Может кто-нибудь мне помочь?
Это действительно стандартная проблема, а не какая-то сложная проблема массового компилятора (хотя я не могу понять, как компиляторы могут разрешить эту ситуацию)?
1 ответ
Строго ответить на ваш вопрос
Я не могу найти ни одного пункта в стандарте C++, который мог бы сказать что-либо из этого неверного / плохо сформированного кода. Может кто-нибудь мне помочь?
Это [basic.def.odr]/12.2
В программе может быть более одного определения типа класса [...] при условии, что каждое определение появляется в отдельной единице перевода, и при условии, что определения удовлетворяют следующим требованиям. Если такой объект с именем D определен более чем в одной единице перевода, то
- каждое определение D должно состоять из одинаковой последовательности токенов; а также
в каждом определении D соответствующие имена, которые ищутся в соответствии с [basic.lookup], должны ссылаться на объект, определенный в определении D, или должны ссылаться на тот же объект после разрешения перегрузки и после сопоставления частичной специализации шаблона ([temp.over]), за исключением того, что имя может ссылаться на
- [... ничего не актуально]
В вашей программе FoobarUser
определяется в обеих ваших единицах перевода, но имя Foobar
в рамках относится к --- в соответствии с правилом безусловного поиска --- двух разных лиц (moo::test::FooBar
а также moo:FooBar
). И это нарушает правило единого определения. Диагностика не требуется.