#c #sizeof #one-definition-rule #unqualified-name
#c #sizeof #one-definition-rule #неквалифицированное имя
Вопрос:
Вот пример:
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: %zdn", size);
return malloc(size);
}
} // namespace moo::test::xxx
int main()
{
new moo::test::xxx::X;
printf("Actual size: %zd, member size: %zdn", 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: %zdn", sizeof(*this));
}
moo::test::xxx::FoobarUser::~FoobarUser()
{}
Итак, что здесь происходит: в зависимости от порядка включения неквалифицированное имя разрешается в разных типах, и в FoobarUser.cpp
мы получаем size 64
, в Main.cpp
мы получаем size 32
. Не только sizeof
отличается — operator new
вызывается с неправильным ( 32
) размером, но конструктор инициализирует размер 64
, который приводит к повреждению памяти.
Как в clang, так и в msvc результатом этой программы является:
Allocated size: 32
FoobarUser constructor, size: 64
Actual size: 32, member size: 32
Это звучит очень подозрительно и в основном означает, что неквалифицированные имена недопустимы при столкновении имен, потому что в зависимости от порядка включения это может привести к тому, что по сути является неверной программой.
Но я не могу найти ни одного пункта в C std, который бы указывал на какой-либо из этих недопустимых / неправильно сформированных кодов. Кто-нибудь может мне помочь?
Действительно ли это стандартно, а не какая-то сложная проблема массового компилятора (хотя я действительно не вижу, как компиляторы могут разрешить эту ситуацию)?
Комментарии:
1. Я не понимаю, почему это вас удивляет. Когда вы вызываете свой компилятор для компиляции
Main.cpp
, через включения он получает определениеFoobar
, которое отличается от того, которое видитFoobarUser.cpp
, когда вы вызываете свой компилятор для компиляции этой последней единицы перевода. И да, это, очевидно, проблема.2. Я имел в виду «найти с помощью неквалифицированного поиска» под «увиденным» или «получает». Мой плохой.
3. Да, я понимаю, что это «логично», за исключением того, что это приводит к некорректному коду и нет предупреждения или диагностики, которые скажут вам «размер отличается, вы собираетесь прострелить себе ногу». Плюс std обычно говорит что-то вроде «если вы делаете X, программа неправильно сформирована, диагностика не требуется»
4. нарушение ODR @AndrianNord не совсем поддается проверке реализациями. Потребовалась бы действительно дорогая и сложная программа для обнаружения большинства из них, но не всех
Ответ №1:
Чтобы строго ответить на ваш вопрос
Я не могу найти ни одного пункта в C std, который бы указывал на какой-либо из этих недопустимых / неправильно сформированных кодов. Кто-нибудь может мне помочь?
В программе может быть более одного определения типа класса […] при условии, что каждое определение отображается в разных единицах преобразования, и при условии, что определения удовлетворяют следующим требованиям. Учитывая такой объект с именем D, определенный более чем в одной единице перевода, тогда
- каждое определение D должно состоять из одной и той же последовательности токенов; и
в каждом определении D соответствующие имена, просматриваемые в соответствии с [basic.lookup], должны ссылаться на объект, определенный в определении D, или должны ссылаться на тот же объект, после разрешения перегрузки и после сопоставления частичной специализации шаблона ([temp.over]), за исключением того, что имя может ссылаться на
- [… ничего существенного]
В вашей программе FoobarUser
определено в обеих ваших единицах перевода, но имя Foobar
внутри ссылается на — согласно правилу неквалифицированного поиска — две разные сущности ( moo::test::FooBar
и moo:FooBar
). И это нарушает правило одного определения. Диагностика не требуется.
Комментарии:
1. Да, у меня было подозрение, что это ODR, но я ожидал, что «это нарушение ODR». Мне не показалось, что нужно искать определение odr. Спасибо