C ++ поиск безусловного имени: разный размер структуры в разных cpp приводит к тому, что оператор new выделяет меньше памяти, чем процессы конструктора? - PullRequest
0 голосов
/ 07 марта 2019

Вот пример:

Main.cpp * * 1004

#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 Ответ

4 голосов
/ 07 марта 2019

Строго ответить на ваш вопрос

Я не могу найти ни одного пункта в стандарте C ++, который мог бы сказать какой-либо из этого недействительного / плохо сформированного кода. Кто-нибудь может мне помочь?

Это [basic.def.odr]/12.2

В программе может быть несколько определений типа класса [...] при условии, что каждое определение появляется в другой единице перевода, и при условии, что определения удовлетворяют следующим требованиям. Если такая сущность с именем D определена более чем в одной единице перевода, то

  • каждое определение D должно состоять из одной и той же последовательности токенов; и
  • в каждом определении D соответствующие имена, найденные в соответствии с [basic.lookup], должны относиться к объекту, определенному в определении D, или должны ссылаться на тот же объект после разрешения перегрузки и после соответствие частичной специализации шаблона ([temp.over]), за исключением того, что имя может ссылаться на

    • [... ничего не актуально]

В вашей программе FoobarUser определено в обеих ваших единицах перевода, но имя Foobar в пределах относится - в соответствии с правилом неквалифицированного поиска - к двум различным сущностям (moo::test::FooBar и moo:FooBar). И это нарушает правило единого определения. Диагностика не требуется.

...