Здравствуйте, nen777w, Вы писали:
N>Такая проблема. N>есть совершенно два разных cpp файла в которых в namespace определена одна и та же структура:
у вас структуры разные определяются, хотя имена совпадают, имеют внешнее связывание
получаем UB
вам нужно убрать совпадение имен (у вас клеш)
чего вы ожидали от этого кода?
wander:
W>Я даже больше скажу: ключевое слово static не рекомендуется к использованию в таком контексте: W>
W>7.3.1.1/2
W>The use of the static keyword is deprecated when declaring objects in a namespace scope, the unnamed-namespace provides a superior alternative.
Здравствуйте, nen777w, Вы писали:
N>структуры то определяются в cpp файлах. наружу они никак не видны. это локальные типы конкретного модуля компиляции. разве не так?
они видны наружу, поэтому конфликтуют
поместите их в анонимный неймспейс, тогда клеш пропадет
Так как конструктор объявляется в теле класса, он неявно становится inline. Эксемпляр конструктора будет в каждой единицы трансляции, где используется. В некоторых случаях функция будет встроена, в других — нет. Определения одной и той же функции должны быть одинаковы, иначе — undefined behaviour. Visual Studio, похоже, на этапе линковки молча выбирает одну из реализаций.
Здравствуйте, nen777w, Вы писали:
N>есть совершенно два разных cpp файла в которых в namespace определена одна и та же структура:
Нарушение ODR.
Если имена менять не хочется, преодолеть это поможет дополнительное оборачивание их в анонимное пространство имён.
Здравствуйте, nen777w, Вы писали:
J>>Как ты думаешь, с таким хедером ты имеешь право реализовывать структуры по-разному? J>>А теперь подумай, это чем-нть отличается от твоего кода, с учетом того, что содержимое хедеров просто включается препроцессором в файл как текст?
N>да, да это Я только потом подумал по сути после препроцессинга собирается один огромный модуль, который передается на вход компилятору, который и подготавливает obj модули с информацией для линкера, и там уже для него нет различия внутренний/наружный.
Нет, почему же, там различие есть как раз. Все, что с external linkage, будет наружным, все, что с internal — видно не будет.
N>но warning наверно мог бы и показать, у меня не показывал. N>кстати это ведь не правильно (warning level правда дефолтный), но ведь это такая скрытая подляна получается?
Ну это ты линкеру предлагаешь нехило поработать так... Т.е. вот есть класс, у него с десяток методов. И линкер долджен определить, что у него этот десяток методов, потом каждый с каждым сравнить насквозь через все объектники, которые он линкует (а их может быть несколько тысяч), и посмотреть, все ли одинаково реализованы (а оно может и не быть одинаково, например, регистры по-другому легли или NOP-ов насовали для выравнивания в одном месте и не насовали в другом). Ведь стандарт не требует, чтобы из одного и того же кода всегда получался один и тот же асм. Не говоря уже о случаях встраивания. Причем просто сравнить каждый с каждым недостаточно, нужно ловить отсутствующие вещи из-за перегрузки и прочих радостей.
Или вот еще пример:
// a.cppstruct Rect
{
int l,r,t,b;
int area() const;
};
int Rect::area() const { return (r-l)*(b-t); }
// b.cppstruct Rect
{
int l,r,w,h;
int area() const;
};
//int Rect::area() const { return w*h; }
Обрати внимание, на уровне информации, доступной линкеру, и там, и там 4 инта. И там объявлен Rect::area с одинаковой сигнатурой. Только во втором файле ты забыл ее определить. Ну и что делать линкеру? По сигнатурам все хорошо, можно брать реализацию из a.cpp, которая делать абсолютно не то. А даже если и не забыл определить — может, они одинаковые на самом деле, просто компилятор по-разному к ним отнёсся?
Так что заведи себе правило — всё, что пишется внутри .cpp для его внутренних нужд: хелперы, функторы и т.п. — всё всегда должно лежать в анонимном пространстве имен. Вне этого пространства должны лежать только определения того, что объявлено в хедерах, и функция main. Остальное — прятать.
Здравствуйте, dilmah, Вы писали:
D>и причем здесь ODR?
Как причём?
Стандарт, 3.2.5:
// больше одного, но
There can be more than one definition of a class type (clause 9), enumeration type (7.2), inline function
with external linkage (7.1.2), class template (clause 14), non-static function template (14.5.5), static data
member of a class template (14.5.1.3), member function of a class template (14.5.1.1), or template special-
ization for which some template parameters are not specified (14.7, 14.5.4) in a program provided that each
definition appears in a different translation unit, and provided the definitions satisfy the following require-
ments. Given such an entity named D defined in more than one translation unit, then
// ---v — each definition of D shall consist of the same sequence of tokens; and
...
Здравствуйте, dilmah, Вы писали:
D>и причем здесь ODR?
в данном случае мы имеем определение двух одинаковых структур (имена типов совпадают), которые различаются. это нарушает ODR и приводит к неопределенному поведению (которое не обязано диагностироваться)
если бы мы одинаково определили эти структуры в двух .cpp, то нарушения ODR не было бы
Здравствуйте, uzhas, Вы писали:
U>Здравствуйте, nen777w, Вы писали:
N>>Такая проблема. N>>есть совершенно два разных cpp файла в которых в namespace определена одна и та же структура: U>у вас структуры разные определяются, хотя имена совпадают, имеют внешнее связывание U>получаем UB U>вам нужно убрать совпадение имен (у вас клеш) U>чего вы ожидали от этого кода?
погодите не понял.
структуры то определяются в cpp файлах. наружу они никак не видны. это локальные типы конкретного модуля компиляции. разве не так?
Здравствуйте, uzhas, Вы писали:
U>Здравствуйте, nen777w, Вы писали:
N>>структуры то определяются в cpp файлах. наружу они никак не видны. это локальные типы конкретного модуля компиляции. разве не так? U>они видны наружу, поэтому конфликтуют U>поместите их в анонимный неймспейс, тогда клеш пропадет
static не поможет?
Здравствуйте, uzhas, Вы писали:
U>Здравствуйте, blackhearted, Вы писали:
B>>static не поможет? U>а куда выхотите его приписать? U>static namespace? static struct? в этих случаях не поможет
Здравствуйте, uzhas, Вы писали:
U>Здравствуйте, nen777w, Вы писали:
N>>структуры то определяются в cpp файлах. наружу они никак не видны. это локальные типы конкретного модуля компиляции. разве не так? U>они видны наружу, поэтому конфликтуют U>поместите их в анонимный неймспейс, тогда клеш пропадет
вот блин... Я не знал об этом интересном поведении линкера.
Как это правильно называется что бы "ликбез почитать"?
Здравствуйте, nen777w, Вы писали:
N>структуры то определяются в cpp файлах. наружу они никак не видны. это локальные типы конкретного модуля компиляции. разве не так?
Что, по-твоему, означает выражение "видны наружу"?
Прадставь, что у нас есть хедер с forward declaration, который подключается к каждому твоему cpp-файлу:
namespace A
{
struct field_;
}
Как ты думаешь, с таким хедером ты имеешь право реализовывать структуры по-разному?
А теперь подумай, это чем-нть отличается от твоего кода, с учетом того, что содержимое хедеров просто включается препроцессором в файл как текст?
J>Как ты думаешь, с таким хедером ты имеешь право реализовывать структуры по-разному? J>А теперь подумай, это чем-нть отличается от твоего кода, с учетом того, что содержимое хедеров просто включается препроцессором в файл как текст?
да, да это Я только потом подумал по сути после препроцессинга собирается один огромный модуль, который передается на вход компилятору, который и подготавливает obj модули с информацией для линкера, и там уже для него нет различия внутренний/наружный.
но warning наверно мог бы и показать, у меня не показывал.
кстати это ведь не правильно (warning level правда дефолтный), но ведь это такая скрытая подляна получается?
Ответ дан выше, есть еще похожий баг, когда конструкторы срабатывают коректно, а вот из за того что у них есть виртуальные деструкторы, начинается сказка при удалении, багу искали пару дней, потом я нечайно ее заметил
Вопрос такой, разве тяжело давать варнинг/еррор в линкере когда он находит два определения?
Здравствуйте, uzhas, Вы писали:
U>Здравствуйте, nen777w, Вы писали:
N>>структуры то определяются в cpp файлах. наружу они никак не видны. это локальные типы конкретного модуля компиляции. разве не так? U>они видны наружу, поэтому конфликтуют U>поместите их в анонимный неймспейс, тогда клеш пропадет
гм... а как тогда указать компилятору что нужен именно этот конкретный тип.
Я имею в виду такое:
namespace
{
struct S
{
....
}
}
class impl : public impl_t<S> {}; <-- Я так понимаю здесь возникает та же проблема?
Решение поместить эту строчку в этот же анонимный неймспейс?
Здравствуйте, nen777w, Вы писали:
N>гм... а как тогда указать компилятору что нужен именно этот конкретный тип. N>Я имею в виду такое:
N>
N>namespace
N>{
N> struct S
N> {
N> ....
N> }
N>}
N>class impl : public impl_t<S> {}; <-- Я так понимаю здесь возникает та же проблема?
N>
N>Решение поместить эту строчку в этот же анонимный неймспейс?
Зачем? S, упомянутый выше в impl_t<S> и будет из этого анонимного namespace. Анонимный namespace автоматом раскрывается как после using'а именованного.
Здравствуйте, IROV.., Вы писали:
IRO>Здравствуйте, nen777w, Вы писали:
IRO>Ответ дан выше, есть еще похожий баг, когда конструкторы срабатывают коректно, а вот из за того что у них есть виртуальные деструкторы, начинается сказка при удалении, багу искали пару дней, потом я нечайно ее заметил
IRO>Вопрос такой, разве тяжело давать варнинг/еррор в линкере когда он находит два определения?
N>>namespace
N>>{
N>> struct S
N>> {
N>> ....
N>> }
N>>}
N>>class impl : public impl_t<S> {}; <-- Я так понимаю здесь возникает та же проблема?
N>>
N>>Решение поместить эту строчку в этот же анонимный неймспейс? AF>Зачем? S, упомянутый выше в impl_t<S> и будет из этого анонимного namespace. Анонимный namespace автоматом раскрывается как после using'а именованного.
//a.hnamespace TEST
{
//fwstruct a_impl;
class A
{
private:
a_impl* pImpl;
a_impl get_impl() { return pImpl; }
public:
A();
void doo();
}
}
//a.cppusing namespace A;
namespace
{
struct S //имя этой структуры повторяется в нескольких модулях: a.cpp, b.cpp, d.cpp ...
{
S(){}
};
}
namespase A {
struct a_imp : public t_impl<S> {};
}
//конструктор
A::A() : pImp( new a_imp() ) {}
//метод doovoid A::doo()
{
S s; //<-- тут буден вызван правильный конструктор
get_impl()->foo(s); //<-- а вот тут когда мы прийдём в метод t_impl::foo() аргумент какого то фига будет другого типа :xz:
//сужу потому как у него даже будет другой набор полей
}
или меня дебагер дурит или Я чо вообще происходит с линкером
N>namespace
N>{
N> struct S //имя этой структуры повторяется в нескольких модулях: a.cpp, b.cpp, d.cpp ...
N> {
N> S(){}
N> };
N>}
А эти строки:
N>namespase A {
N> struct a_imp : public t_impl<S> {};
N>}
упоминаются только в a.cpp? Или в b.cpp, c.cpp, d.cpp есть такое же определение a_imp(l?)?
(На всякий случай: rebuild после оборачивания всех S в anonymous namespace был сделан?).
AF>упоминаются только в a.cpp? Или в b.cpp, c.cpp, d.cpp есть такое же определение a_imp(l?)? AF>(На всякий случай: rebuild после оборачивания всех S в anonymous namespace был сделан?).
уже разобрался, написал постом выше. всё нормально с кодом.
это у дебагера едет крыша, а Я ему верю.
7.1.2
3- A function defined within a class definition is an inline function. The inline specifier shall not appear on a block scope function declaration.*
4- An inline function shall be defined in every translation unit in which it is used and shall have exactly the same definition in every case (basic.def.odr).
Как много веселых ребят, и все делают велосипед...
При включенной оптимизации и совпадении sizeof(float) и sizeof(int) тело метода m1 и m2 будет одно на двоих, как и адрес в vtable обоих типов. Имена тут не при чем.
Здесь нет никакого бага, это же внутреннее дело компилятора. Тоже самое наблюдается для численных литералов, уже обсуждалось: http://rsdn.ru/forum/flame.comp/3782092.1.aspx
Без такой оптимизации мы бы имели проблему как в первых компиляторах С++, поддерживающих шаблоны — очень большой бинарник. А тут имеет место "склейка" идентичных участков кода, поэтому готовый бинарник, порождаемый MSVC порой более чем в 2-3 раза меньше, чем порождаемый gcc.
Здравствуйте, vdimas, Вы писали:
V>Здравствуйте, nen777w, Вы писали:
N>>Такая проблема. N>>есть совершенно два разных cpp файла в которых в namespace определена одна и та же структура:
V>Ну так что пугает? Пользовательский конструктор у них одинаковый.
Тут не пугает, тут бага.
твой пример это оптимизация, которая не влияет на семантику.
А в случае автора — баг, который меняет семантику(ожидаемый результат)
Здравствуйте, IROV.., Вы писали:
IRO>Вопрос такой, разве тяжело давать варнинг/еррор в линкере когда он находит два определения?
Есть такая вещь как инстанциация шаблонов. Например, в разных единицах трансляции несколько раз инстанцируется std::max<int>(). Задача линкера, уже ничего не знающего о шаблонах, дать выжить только одной инстанциации в конечном исполняемом файле. Думаю, поэтому затруднительно выдавать предупреждение в каких-то специфичных случаях: не очень ясно, как можно быстро проверить эквивалентность функций. Впрочем, как это сделать не быстро, тоже не ясно .
Здравствуйте, blackhearted, Вы писали:
B>Здравствуйте, uzhas, Вы писали:
U>>Здравствуйте, blackhearted, Вы писали:
B>>>static не поможет? U>>а куда выхотите его приписать? U>>static namespace? static struct? в этих случаях не поможет
B>точно это же definition... признаю совю неправоту
Я даже больше скажу: ключевое слово static не рекомендуется к использованию в таком контексте:
7.3.1.1/2
The use of the static keyword is deprecated when declaring objects in a namespace scope, the unnamed-namespace provides a superior alternative.
Здравствуйте, Dmi3S, Вы писали:
DS>Здравствуйте, IROV.., Вы писали:
IRO>>Вопрос такой, разве тяжело давать варнинг/еррор в линкере когда он находит два определения?
DS>Есть такая вещь как инстанциация шаблонов. Например, в разных единицах трансляции несколько раз инстанцируется std::max<int>(). Задача линкера, уже ничего не знающего о шаблонах, дать выжить только одной инстанциации в конечном исполняемом файле. Думаю, поэтому затруднительно выдавать предупреждение в каких-то специфичных случаях: не очень ясно, как можно быстро проверить эквивалентность функций. Впрочем, как это сделать не быстро, тоже не ясно .
с функциями проблем никогда небыло, были проблемы с методами
Здравствуйте, IROV.., Вы писали:
IRO>Здравствуйте, Dmi3S, Вы писали:
DS>>Здравствуйте, IROV.., Вы писали:
IRO>>>Вопрос такой, разве тяжело давать варнинг/еррор в линкере когда он находит два определения?
DS>>Есть такая вещь как инстанциация шаблонов. Например, в разных единицах трансляции несколько раз инстанцируется std::max<int>(). Задача линкера, уже ничего не знающего о шаблонах, дать выжить только одной инстанциации в конечном исполняемом файле. Думаю, поэтому затруднительно выдавать предупреждение в каких-то специфичных случаях: не очень ясно, как можно быстро проверить эквивалентность функций. Впрочем, как это сделать не быстро, тоже не ясно .
IRO>с функциями проблем никогда небыло, были проблемы с методами
А есть разница между функциями и методами на стадии линковки?
Здравствуйте, Dmi3S, Вы писали:
DS>Здравствуйте, IROV.., Вы писали:
IRO>>Вопрос такой, разве тяжело давать варнинг/еррор в линкере когда он находит два определения?
DS>Есть такая вещь как инстанциация шаблонов. Например, в разных единицах трансляции несколько раз инстанцируется std::max<int>(). Задача линкера, уже ничего не знающего о шаблонах, дать выжить только одной инстанциации в конечном исполняемом файле. Думаю, поэтому затруднительно выдавать предупреждение в каких-то специфичных случаях: не очень ясно, как можно быстро проверить эквивалентность функций. Впрочем, как это сделать не быстро, тоже не ясно .
предположительно это могли бы быть подсказки от компилятора, ему то об этом известно всё, хотя может Я не могу охватить всех специфических случаев.
Маленькое уточнение. Ты все правильно говоришь, но, возможно, не совсем точно — при чтении твоих постов может сложиться неверное мнение, будто бы все сущности, определенные в анонимных пространствах имен, автоматом получают внутреннее связывание. В анонимных пространствах имен действуют те же правила, что и в обычных — константы по умолчанию получают внутреннее связывание, функции, переменные и классы — внешнее. Только сами пространства имен изолированы от внешнего мира.
Здравствуйте, nen777w, Вы писали:
N>предположительно это могли бы быть подсказки от компилятора, ему то об этом известно всё, хотя может Я не могу охватить всех специфических случаев.
Компилятору известно далеко не все, а только единица трансляции. Да и какие "подсказки" ему давать линкеру, у которого на входе уже действительно все, даже с излишком?