Pull to refresh

Comments 346

У меня же не было цели тестировать хабрапользователей :)

Важно для себя сначала понять какая функция вызвана, если B::Foo, то Вы дали верный ответ.

Но довольно часто отвечают A::Foo, n = 10. Это значит, что у разработчик не понимает, что такое виртуальные функции. Сначала надо с ними разобраться, а тогда станет понятен в чем прикол этого примера.

Это у меня была любимая задачка на собеседовании для разработчиков C++ с опытом выше среднего.
Это, как раз, значит, что разработчик ЗНАЕТ, что такое виртуальные функции. Вы вызываете метод для класса А, который ничего не знает о классе B и его параметрах по-умолчанию. И если программист отвечает 20, то это повод задуматься ему — а правильно ли он понимает сущность виртальных функций в C++, ибо, скорее всего у него возникнут проблемы и с виртуальными конструкторами/дуструкторами и вообще с работой с виртальными функциями в необычных ситуациях.
вы невнимательно прочитали коммент;) человек имел в виду что если отвечают что вызвалась функция из А, то отвечающий не знает. Это абсолютно верно. А вот вторая часть ответа (10 или 20) на самом деле заслуживает варнинга компилятора.
В принципе, ответ напрашивается сам собой, когда начинаешь думать над кодом с позиции компилятора. Но с первого взгляда действительно не до конца очевидно. Спасибо за интересную заметку )
Я это знал. Что со мной не так. Но про предупреждение согласен.
Тогда все ОК, если, конечно, вы в ответ не подглядывали :) По моему опыту не больше 10% людей с опытом работы на C++ уверенно дают правильный ответ.
Самое забавное, что скорее не опытом работы, а опытом разбора килотон мегакода. И вообще давно это было. Мигрировал я в область Javascript, и обратно не собираюсь.
Сходу дал правильный ответ. Исходил из того, что значение по умолчанию к виртуальности не имеет никакого отношения, т.к. подставляется во время компиляции.
И вообще эту фичу в основном для шаблонов сделали. Использование параметров по умолчанию в виртуальных\статичных методах в С++ это жесть.
Ну не только для шаблонов, еще очень многие любят, например, конструкторы снабжать большим числом параметров, но со значениями по умолчанию. К счастью конструкторы не бывают виртуальными.

Согласен с Вами, что в виртуальных функциях их просто нельзя использовать. Вопрос напрашивается, почему в языке не запретили такое использование. Разрешили бы только для невиртуальных функций, например.
В стандартах разных версий, компиляторах разных версий\производителей такое количество сложносочинённых глюков, что их можно обсуждать до исчерпания места на серверах хабры.
По мне так вообще все верно. Если учесть идею виртуальных методов в связке Интерфейс-ВиртуальныеРеализации.

Как правило программист-пользователь имеет на руках некий интерфейс из хедера (ну скажем ISomething), и хоть, де факто, при использовании, будут вызываться виртуальные методы потомков, но вот инициализация параметров берется та, что объявлена в интерфейсе. Хедеры реализации вообще могут (и должны) находиться в приватной области для кода реализаций, так что какие там инициализаторы и есть ли оны — узнать невозможно (опять же, с точки зрения пользователя интерфейса).

Хотя варнинг может и не помешал бы :)
Допустим, что инициализация параметров по умолчанию должна быть частью интерфейса, а не реализации. Почему язык мне вообще разрешает в таком случае прописать параметр по умолчанию в классе реализации?
Вы можете не указывать при вызове параметры по умолчанию всюду, где видно определение функции с параметрами по умолчанию. Если вы добавите параметр по умолчанию в реализацию, вы сможете использовать вызов функции без указания этого параметра например внутри самой этой функции. Внешние же клиенты будут обязаны параметр указывать.

Пример:
// a.h
class A
{
  public:
    void foo(int n);
    void bar();
    void baz();
};

// a.cpp
#include "a.h"
void A::bar()
{
  foo(); // ошибка
}

void A::foo(int n = 0)
{
  if (n)
    foo(); // OK
}

void A::baz()
{
  foo(); // OK
}

// b.cpp
#include "a.h"
int main()
{
  A a;
  a.foo(); // ошибка
}
Точнее, B::Foo, n = 10

И поведение компилятора, который подставляет значение 10, вполне легко объяснить.

При переопределении виртуальной функции всё, что меняет потомок в родителе — это адрес функции, хранящийся в области памяти экземпляра. Никакой дополнительной информации больше потомок туда не вкладывает (ну разве что если не включен RTTI или прочие навороты для позднего связывания).
Поэтому компилятор и не может на основе просто указателя на базовый тип A взять такую информацию, как значение параметра по умолчанию.

С другой стороны, количество параметров при вызове функции всегда должно быть постоянным и должно соответствовать сигнатуре функции, поэтому при каждом вызове компилятор должен положить в стек столько чисел, сколько аргументов у функции.

Более того, совершать догадки вроде «Да тут же присвоен указатель на дочерний класс B! Дай-ка я другое значение по умолчанию подставлю» компилятору категорически нельзя делать! Ведь поведение строки
A * pa = new B();
не должно зависеть от правого выражения и, по логике, должно быть идентичным поведению такой строки:
A * pa = GetSomeAChild();
где GetSomeAChild — функция, возвращающая указатель типа A*, а уж какой на самом деле там тип возвращён — A, B или другой их потомок с ещё одним значением параметра по умолчанию — никто не знает.

Отсюда и вполне логичное требование в документации руководствоваться типом указателя.

P.S. Кстати, в C# 3.0 от правого выражения действительно может зависеть тип переменной, если описывать её как
var ds = new DataSet();
Но это C#, в нём упрощается код и усложняется среда исполнения и компилятор, а в C++, наоборот, простое исполнение при сложном коде.
Вы все безусловно верно написали!

Единственное маленькое уточнение, стандарт языка никак не регламентирует устройство механизма вызова виртуальных функций. Вполне допустима реализация компилятора, которая вообще не будет создавать таблиц указателей виртуальных функций, а, например, (моя чистая умозрительная спекуляция) будет работать через какой-нибудь глобальный объект-словарь. Никому в здравом уме это в голову не придет, но ничего незаконного в этом нет.

Соответственно механизм, который находит адрес соответствующей виртуальной функции для выполнения, мог бы быть расширен для хранения и значений параметров по умолчанию. Например, в vtable положить не только адреса функций, а и значения по умолчанию рядышком, как в стеке вызова.

Тем не менее, авторы стандарта языка сознательно пошли на отказ от «виртуализации» значений по умолчанию. Т.е. их «невиртуальное» поведение является обязательным с точки зрения стандарта (см. тот пункт из стандарта, который я указал в тексте хабратопика).

То есть, текущее поведение компилятора отнюдь не является чем-то единственно принципиально возможным. Если бы не странное требование стандарта, вполне могло быть и по-другому. (Правда с этим не согласны поборники принципа «значения по умолчанию — часть интерфейса, а не реализации».)
Не нужно там ничего расширять. А насчёт сознательного отхода — что-то я сильно сомневаюсь. Больше похоже что вначале сделали как проще, а потом описали что получилось. То, что вначале в обработке этих параметров по умолчанию было много несуразностей и это дело долго причёсывали указывает именно на эту возможность…
Чёрт подери, магия! Когда смотрю на ваш комментарий боковым зрением, то там, где GetSomeAChild появляется диагональная полоска. Минуту водил взгляд туда-сюда.
Да ну, чего неожиданного, откуда компилятор узнает про 20 если он в месте вызова располагает только vtable и знанием того что pa это экземпляр A?

Чтоб оно подставляло 20 в качестве параметра, надо было бы это дефолтное значение вместе с vtable хранить каким-то хитрым способом.
UFO just landed and posted this here
Тогда это будет неконтролируемый оверхед, что не очень хорошо вписывается в парадигму C++
Да и не нужно это. Такой код просто писать не стоит и все. Как правильно выше заметили — параметры по умолчанию в виртуальных методах это плохо.
> Тогда это будет неконтролируемый оверхед
Неужели он больше по времени за все предполагаемое кол-во запусков на отдельно взятой машине, чем время на отладку?)
Ну просто в C++ в этом нет нужды. :-)

Если нужны такие фишки, которые в C++ выглядят оверхедом — советую обратить внимание на C# и Java. В них отражение, упаковка (и прочие фантастические для C++ вещи) — просто неотъемлимая часть среды и языка. И отлаживать их значительно проще, и ошибки уже другого уровня, не GPF при чтении неправильного указателя, а просто исключения.

Однако, холивара не будет, потому что C++ и C# — действительно разные языки, у каждого своя ниша.
для с++ нет фантастических вещей, посмотрите на boost :-) и что в нём творят :)
Boost может многого, но вот одной простой вещи не может: взять класс и как-то его обработать. Ибо не поддерживает этого C++ — выше головы не прыгнешь. Сравните jMock и gMock если хотите понять — зачем это может быть нужно…
произвольный класс, конечно нет, но есть boost::fusion :-) который позволяет автоматизировать создание классов и их обработку
А какой смысл обрабатывать класс, который вы сами делаете? Смысл использовать reflection имеет в основном для сторонних библиотек как раз.
ну здрасте, от серализации/конвертирования, до манипуляции типами, полями, полями типов и тп

задачи такие возникают регулярно как только разработчик упирается в работу с абстрактным кодом.
Тьфу. Конечно имелся в виду C++ gMock
Надо заметить, наследование с замещением методов + параметры по-умолчанию = уже хреновый звоночек
Да кто бы спорил! В моем личном списке правил кодирования на это вообще жесткий запрет (виртуальная функция с параметрами по умолчанию).

Философский вопрос тут возникает, а правильно ли устроена вселенная, если при полном отсутствии каких-либо напрягов в стандарте языка и у компиляторов по поводу этой ситуации приходится такое жесткое правило себе придумывать… Сделаю еще топик по поводу C# и Java — там все тоже очень интересно происходит.
Чего в C# и Java то интересного, там ведь авторы решили не усложнять себе и юзерам жизнь, и от дефолтных параметров отказались :)
Ну… в C# 4.0 они передумали :) и там все очень чудесато получилось. Забавно пронаблюдать, как мнение Андерса Хейсльберга по этому вопросу плавно эволюционировало.

А в Java их действительно нет (пока??) и опять же интересно мнение коммъюнити на эту тему и обоснование дизайнеров языка.
Ухты, и правда, еще и именованные параметры добавили, красота…
> А в Java их действительно нет (пока??) и опять же интересно мнение коммъюнити на эту тему и обоснование дизайнеров языка.

Параметры со значениями по умолчанию не нужны, потому что
а) класс должен предоставлять минимально возможный интерфейс
б) функции со значениями по умолчанию это ломают

Всегда легче написать функцию вне класса, которая как раз таки и будет вызывать нужный метод с правильными значениями по-умолчанию. То же самое относится и к конструкторам, которых обычно целую кучу пишут, на все случаи жизни.
Простая логика, если переменную объявили с типом А, то она и должна так выглядеть, неважно, что внутрь положили.
Совершенно согласен. Нельзя путать тип с классом.
>> По моему опыту не больше 10% людей с опытом работы на C++ уверенно дают правильный ответ.

Ну либо это люди не использующие ООП, либо процент должен быть побольше. Ответ все-таки лежит на поверхности.
Ответ malek чуть выше в разы понятнее обьясняет суть этого «нонсенса» :)

Наследование и параметры по умолчанию — всегда ведут за собой неудобства, а иногда и вот такие непонятки.
>Очень интересно в этой связи посмотреть на параметры по умолчанию в других популярных языках.

В Питоне параметры по умолчанию работают точно так же, но из-за нестрогой типизации переменных подобный вопрос вообще не возникает.
#! /usr/bin/env python3.0

class A:
    def foo(self, n=10):
        print("A.foo, n = ", n)

class B(A):
    def foo(self, n=20):
        print("B.foo, n = ", n)

pa = B()

if isinstance(pa, A): # true
    pa.foo() # B.foo, n = 20

Хотя неизвестно, хорошо это или плохо.
В Python аналогичная ситуация возникает, т.к. параметры по умолчанию инициализируются только один раз «при описании класса».
Да, очень неприятный случай. Надеюсь, что в C# 4.0, в котором должны появится параметры по-умолчанию, эта проблема будет решена.
UFO just landed and posted this here
С#, вообще-то, придумывался для того, чтобы не было таких подводных камней и прочих хитрых ловушек. Т.е. чтобы люди не делали глупых ошибок и не тратили время (!) на их исправление — вот что главное.
UFO just landed and posted this here
Конечно оправдывает. Удобней синтаксис, удобней компилятор, удобней библиотека, удобней среда. Это все способствует созданию более качественного кода, при прочих равных.
Извините за занудство, но ожидаемо :) Об этом еще Майерс писал в Exceptional C++. Понятное дело, что полиморфизм — это механизм времени исполнение, когда какой параметр передать, компилятор определяет еще во время компиляции. По этому не используйте значения по умолчанию для виртуальных функций :) Не вы, как говорится первый попались, не вы последний :)
Про Майерса согласен с Вами, стоило мне его упомянуть. И разъяснили Вы все верно.

Вопрос, на который я не могу найти пока ответа, почему Страуструп с коллегами не запретили параметры по умолчанию в виртуальных функциях. Или уж, по-крайней мере, не потребовали от компиляторов следить за тем, чтобы дефолтные значения уже нельзя было прописать в производном классе.

Ну ладно в стандарте 1997 года. Книжка Майерса вышла в 1996-ом, еще не все успели прочесть :) Но к 2009-ому уж можно было бы озаботиться этой банальной проблемой?

Я еще одна загадка — почему компиляторы отмалчиваются на этот счет.
а что в них зазорного? вы не можете вызывать напрямую метод дочернего класса в дочернем объекте?

B b;
b.Foo();

а компиляторы варнинги не пишут, потому что ничего противозаконного в этом коде нет.
Ха! Так на противозаконный код пишут не варнинги, а ошибки :)

А варнинги как раз и нужны на законный, но потенциально опасный, неинтуитивный и пр. код, типа пресловутого if (x = 0) {

Разве нет?
тут вопрос «неинтуитивный», для понимающего как работает с++ этот код интуитивный, народ в комментариях написал. Вы неверно оцениваете масштаб проблемы. ИМХО.
Число людей, кто посчитал это поведение «интуитивным» в комментариях — единицы (уж точно не десятки). По сравнению с число просмотров и одобрений топика — очень мало.

Что еще раз подтверждает мой исходный тезис, что большинство разработчиков средней квалификации, не сталкивавшихся до того с подобной конструкцией, не находят ее интуитивной и им все-таки требуется время (пусть и небольшое), чтобы в голове сопоставить «виртуальность» вызова функции с «невиртуальностью» подстановки параметров.

Еще раз повторю, что из моего опыта собеседования кандидатов средней квалификации (около 100 человек), не более 10% давали сходу верный ответ.

И еще раз хочу заверить Вас, чтобы мы больше к этому не возвращались, что c законностью данного поведения C++ никто не спорит и, тем более, это не вызывает ни малейшего сомнения.
число людей действительно знающих с++, а не просто пишущих на нём? К нам регулярно приходят устраиваться люди, которые пишут на с++ и пишут в анкете 100% знание языка, когда начинаешь выяснять, многие не знают вообще как именно работает виртуальность, не говоря уже о более «продвинутых» и глубоких вещах. А вы сравниваете с массой хабра, которые в большинстве своем вебпрограммеры и дизайнеры…

опять же «средней» квалификации… опять же вопрос упирается не в понятность или интуитивность, а в знание языка, в той же java есть «неинтуитивные» места, в почти любом скриптовом языке «неинтуитивности» полно. Только вся эта неинтуитивность — суть от незнания самого языка. Люди пересаживающиеся с паскаля на Си, удивляются «неинтуитивности оператора =». Незнание специалист должен стремится истреблять, или он не будет специалистом.

Давайте закончим этот тред на позитиве!
Незнание специалист должен стремится истреблять, или он не будет специалистом.

Подписываюсь под этим три раза!!!
h1ppo^3
UFO just landed and posted this here
А я вообще Java програмист, про Си знаю оченно мало, но ответ вполне очевиден был из общих соображений. :)
ответ очевиден для человека, который более менее понимает что такое полиморфизм и как компилер делает свое дело… но в таких случаях варнинг реально бы не помешал, так как нехорошо это параметры по умолчанию в вирт. функциях (можно забыть что функция виртуальная и искать ошибку потом в дебаге)
UFO just landed and posted this here
Вы исходите из того, что значение параметра по умолчанию должно быть подставлено компилятором. Абсолютно верно с точки зрения требований стандарта.

Откуда это требование появилось — вот в чем вопрос!

На мой взгляд, нужно было либо потребовать виртуализировать значения по умолчанию вместе с виртуальными функциями (это, конечно, слабо реально учитывая требования эффективности и т. п.), либо запретить их прописывать в переопределении виртуальной функции (B:foo).
Если вы ответили A::Foo, n = 10, скорее всего, вы новичок в С++.
Охх… если бы! :-)
Мне не понравилось замечание автора, о том что если ктото дает правильный ответ, но он ничего не соображает в виртуальных функциях.

Предлагаю поменять немного задачу, чтобы таких сомнений не возникало!

например тело функции:

A::Foo (int n = 10) {
cout << «result = » << 2*n << endl;
}

B::Foo (int n = 20) {
cout << «result = » << 3*n << endl;
}

Таким образом сразу знание обоих вопросов проверяется.
Вообще-то в данном случае тоже проверяется знание обоих вопросов — но раздельно. И вариант «A::Foo, n = 10» правильным ну никак не может являться. Ибо правильный вариант — это «B::Foo, n = 10». Вы разницу между буквами «A» и «B» знаете? Вам букварь, может быть, подарить?
Букварь мне ваш не нужен. Не обязательно так резко реагировать. Да, пардон, не обратил внимание что там еще буквы спрашиваются в задаче. Но тот вариант который привел я даст ответ однозначный, без необходимости потом у опрошаемых спрашивать «А вы какую там еще букву проставите „А“ или „B“»?
Согласен, методически Ваш вариант гораздо лаконичней!!!
очевидно что будет выводится B:foo 10, виртуальная функция, выбор которой производится на этапе работы программы, и параметр по-умолчанию, выбор которого производится на этапе компиляции, по сигнатуре функции, а сигнатура A::foo
Я правильно понял, что вы ВНЕЗАПНО прочитали Effective C++: 55 Specific Ways to Improve Your Programs and Designs?
Нет, неправильно :)

Я первый раз столкнулся с этой проблемой (это когда два дня багу искал) в одна тысяча девятьсот девяносто восьмом году :) В тот момент я еще Майерса не читал, поскольку его первая книжка в самой первой редакции вышла в 1996.

Потом читал, вернее перелистывал. Мне не очень понравилось, что многие случаи разбираются с одной стороны слишком категорично, с другой стороны без достаточно глубокого и разностороннего анализа. Ведь у многих проблем нет очевидного решения в стиле «так писать нельзя». То есть иногда ничего другого не остается, к сожалению (как, например, в разбираемом здесь случае), но все-таки хотелось бы подойти к этому выводу после того, как исчерпывающе проанализированы все альтернативы.

Написал и подумал сам про себя, что в своей писанине далеко ведь не все возможные случаи разобрал :) Прошу прощения, исправлюсь!

Вернее, по задумке — это только первая публикация на эту тему. Дальше планировал написать про дефолтные параметры в C# 4.0 и потом уже отдельный топик с анализом всей проблемы.
UFO just landed and posted this here
тут вы путаете то как «оно хотелось бы мне» и то как оно должно работать по логике вещей.

давайте распишем эту строчку более формально и вы всё поймёте:

pa->Foo ();

(A*)(pa)->(&A::Foo)()

какая у нас функция должна вызываться в этой строчке? Очевидно что A::Foo, программист явно написал что A::Foo() имеет параметр по умолчанию равный 10. Почему компилятор должен выдумывать чтото ещё?
UFO just landed and posted this here
и что? :) сигнатура функции — это фактически её описание, представте что A::Foo это интерфейс, который вы видите в заголовочном файле. B::Foo это реализация этого интерфейса, которая от вас спрятана. Вы каким-то способом получили в своё владение указатель на B, что вы будете ожидать, не видя описания B::Foo от вызова

pa->Foo()?

:-) ответив для себя на этот вопрос, вы поймете почему сделано именно так а не иначе
UFO just landed and posted this here
проблема как раз в этом и есть :)

вот вам еще пример:
struct A{
void Foo(int n=10)=0;
};

struct B:virtual A{
void Foo(int n=20)=0;
}

strut C: B, virtual A{
void Foo(int n){… };
}

A *pa=new C;
pa->Foo();

что будет по вашему будет? :)
UFO just landed and posted this here
у меня откомпилировалось на gcc-4.3.2 без ошибок

да вот потому что нет в c++ перекрывающих определений виртуальных функций :-)
UFO just landed and posted this here
ну мою опечатку то могли бы и сами исправить, или уже будете переходить на другую тему? конечно же в A она виртуальная, что по контексту понятно
Будет 10. И по правилу TolTol было бы 10. А если бы C указала бы новым дефолтным параметром 30 — было бы 30.

Поймите вы — это правило абсолютно непротиворичиво и ни с чем не конфликтует. Как с постулатом о параллельности прямых. Приняли бы его — был бы другой C++ (и мне бы он нравился больше). Но… не приняли… Теперь-то уж чего?
противоречиво :) конфликтует даже с тем что сказал страуструп, неговоря уже о семантике языка, когда мы распишем полную запись вызова:

A *pa;

(pa->*(&A::Foo))()
и
pa->Foo()
получаются логически разные вещи по вашему, хотя это одно и тоже исходя из логики языка с++
получаются логически разные вещи по вашему, хотя это одно и тоже исходя из логики языка с++
Вы идиот или притворяетесь? pa->Foo() — это вызов функции Foo(), (pa->*(&A::Foo))() — это синтаксическая ошибка. Вы не можете в этом примера написать что-нибудь типа:

void (A::*fooPtr)() = &A::Foo;

Это будет попыткой преобразовать void (A::*fooPtr)(int) в void (A::*fooPtr)() — что запрещено. Параметры по умолчанию участвуют только в вызове функции, вы уж извините — так что все ваши «полные записи» — это полная чушь.
давайте без наездов…

я написал семантику вызова, для того чтобы было понятно что же на самом деле написано в этой строчке, в этой строчке написано, объясняю человеческим языком:

вызвать функцию 'Foo' класса 'A' для представителя этого класса доступного по указателю pa с параметрами по умолчанию.

Заметте в строчке не написано «вызвать функцию Foo класса B» или какого другого класса, откуда компилятор должен решать что надо вызывать с параметрами по умолчанию для B?
вызвать функцию 'Foo' класса 'A' для представителя этого класса доступного по указателю pa с параметрами по умолчанию.
Этого там не написано. Там написано: выяснить как реализована функция Foo для класса A или его потомка, на экземпляр которого указывает pa, и вызвать эту реализацию.

Заметте в строчке не написано «вызвать функцию Foo класса B» или какого другого класса, откуда компилятор должен решать что надо вызывать с параметрами по умолчанию для B?
А как он это делает с самой функцией Foo? Почему компилятор достаточно умён чтобы извлечь из указателя pa информацию про нужный ему вариант функции Foo и недостаточно умён для того, чтобы извлечь из того же указателя информацию о правильном умолчальном параметре?
>Этого там не написано. Там написано: выяснить как реализована функция Foo для класса A или его потомка, на экземпляр которого указывает pa, и вызвать эту реализацию.

эээ и как это компилятор может выяснить какой именно потомок будет по указателю pa в общем случае? :)))

>А как он это делает с самой функцией Foo? Почему компилятор достаточно умён чтобы извлечь из указателя pa информацию про нужный ему вариант функции Foo и недостаточно умён для того, чтобы извлечь из того же указателя информацию о правильном умолчальном параметре?

налицо непонимание процесса, компилятор не извлекает ничего из указателя вообще. Это делается самой программой в процессе исполнения. В общем случае мы вообще не можем сказать какой именно потомок класса будет доступен по указателю во время работы программы. Может C, может D может E, может какойто мультипотомок.
эээ и как это компилятор может выяснить какой именно потомок будет по указателю pa в общем случае? :)))
Компилятор ничего и не выясняет. Его цель — скомпилировать код, который сделает то, что я сказал.

налицо непонимание процесса, компилятор не извлекает ничего из указателя вообще.
Налицо желание притвориться идиотом.

Это делается самой программой в процессе исполнения.
Правильно — но делает-то это код, сгенерированный компилятором!

В общем случае мы вообще не можем сказать какой именно потомок класса будет доступен по указателю во время работы программы. Может C, может D может E, может какойто мультипотомок.
Правильно — но если мы не можем сказать что за функция будет вызвана, то почему вдруг мы должны мочь сказать какой именно «умолчальный параметр» будет тут использован? Какие великие истины рухнут если это знание также будет выявляться во время исполнения?
>Компилятор ничего и не выясняет. Его цель — скомпилировать код, который сделает то, что я сказал.

вы уже противоречите сами себе:

>Там написано: выяснить как реализована функция Foo для класса A

>Налицо желание притвориться идиотом.
очередное хамство…

>Правильно — но если мы не можем сказать что за функция будет вызвана, то почему вдруг мы должны мочь сказать какой именно «умолчальный параметр» будет тут использован? Какие великие истины рухнут если это знание также будет выявляться во время исполнения?

как минимум рухнет понимание программы и поддержка её, вам уже тыщу раз приводили пример в котором наследованые реализации для вас скрыты. При этом вы пишите код вида

pa->Foo()

как вы можете быть уверены что при этом в Foo передается именно то что вы имелли ввиду?
При этом вы пишите код вида

pa->Foo()

как вы можете быть уверены что при этом в Foo передается именно то что вы имелли ввиду?
А как вы можете вообще быть в чём-то в этом случае уверены? У вас должно быть описание библиотеки и там это будет описано. Более того, на практике часто так и делается: есть тупо две функции DoSomething() и DoSomething(Options o) и дефолтное значение опций вам даже не показывают. В случае виртуальных дефолтных параметров это была бы одна функция, а не две…
извините конечно, но вы очень непонятно написали что вы хотели сказать этим комментарием. Если я так понял:

Лично я с семантикой: что написано в интерфейсе который я вызваю, то и используется могу быть абсолютно уверен, что параметром по умолчанию будет именно то что написано в описании класса на котороый ссылается pa (в данном случае A). Вы же конечно ни в чем не можете быть уверены, когда полагаете что параметры по умолчанию изменяются в потомках.
Лично я с семантикой: что написано в интерфейсе который я вызваю, то и используется могу быть абсолютно уверен, что параметром по умолчанию будет именно то что написано в описании класса на котороый ссылается pa (в данном случае A).
И что — от этого знания на вас снисходит космическая благодать или как? Это же значение потом немедленно поступает в функцию, которая делает что-то, над чем вы контроля не имеете!!! Ну и что вам это пустое знание даст?
1) это значение поступает в функцию если вы его не указали, только в этом случае.

2) я лично такой код не пишу, но если такое вижу, то могу придумать только следующее пример этому безобразию:

пусть документацией определено что int A::Foo возвращает значение по позиции переданной в параметре, если параметр равен -1 то возвращает общую количество позициий. Очевидно что в этом случае просится отдельный метод для получения длинны позиции, почему же разработчик сделал так а неиначе — наверное в виду его слабой подготовки
Какой-то натянутый пример. Главное — если убрать из документации число -1, то всем от этого станет только лучше. При вызове без параметров — количество позиций, при вызове с параметром — иформация про позицию. Пусть у нас в потомке есть «скрытые переменные» у которых отрицательные аргументы (-1, -2, -3), а дефолтное значение переопределено в -MAX_INT-1. Кому от этого будет плохо? Только тому, кто «знает» дефолтное значение -1 и явно его указывает в программе — но он сам себе злобный буратина, раз использует недокументированное значение…
я вообщето указал что тот кто вообще такой интерфейс создал должен быть отправлен на дообучение :)

если вам нужна функция без параметров — создайте ее, если вам нужно строго определенное действие — опишите его отдельной функцией. Вобщем рефакторинг плачет. :-)
Не видя описания B::Foo я вообще ничего не могу ожидать ибо я не знаю что эта функция будет делать. Чем одна версия хуже/лучше другой — совершенно непонятно.
одна версия хуже другой, потомучто допускает неоднозначные трактовки в реалиях с++

вспомните что есть ещё указатели на функции члены (собственно первое мое объяснение уже должно вызывать у Вас вопросы по поводу предлагаемой Вами схемы), множественное наследование, дочернее наследование, обрезание классов при присваивании не указателями и тп.
одна версия хуже другой, потомучто допускает неоднозначные трактовки в реалиях с++
Нифига подобного. В книжке Страуструпа прямо предлагалось трактовать дефолтные параметры как создание функций с n-1, n-2 и т.д. параметрами, которые вызывают фанкцию с n параметрами. Никакой беды от того, что эти воображаемые функции будут объявлены виртуальными не случится.

вспомните что есть ещё указатели на функции члены (собственно первое мое объяснение уже должно вызывать у Вас вопросы по поводу предлагаемой Вами схемы), множественное наследование, дочернее наследование, обрезание классов при присваивании не указателями и тп.
И что? В каком месте описанное преобразование вступает с этими правилами в противоречие?
читаем что написано у страуструпа :)

«Того же самого результата МОЖНО БЫЛО добиться при помощи перегруженных функций

void print(int value, int base);
inline void print(int value){print (value, 10);}

Однако, перегрузка делает для программиста менее очевидным тот факт, что цель была — получить ОДНУ ФУНКЦИЮ плюс некоторую КОРОТКУЮ ФОРМУ ЕЕ ЗАПИСИ.»

в том и вступает, я вам написал как компилятор понимает строчку:
pa->Foo
как
(A*)(pa)->(&A::Foo)()
компилятор в этой строчке видит именно вызов функции A::Foo, а не B::Foo, в функции A::Foo четко написан параметр по умолчанию n=10, почему он должен брать его из B? потомучто вы, думая дальше чем копилятор, хотите чтобы компилятор посмотрел глубже и решил что же на самом деле там может быть за указатель, не B::Foo ли? или C::Foo? Но откуда компилятору это знать?

компилятор в этой строчке видит именно вызов функции A::Foo, а не B::Foo

Компилятор видит в этой строчке то, что ему предписано видеть. «Свободы воли» у него нет.
почему он должен брать его из B?
А почему бы и нет?
Но откуда компилятору это знать?
А откуда он знает что ему нужно вызывать B::Foo на самом деле? Из VTable. Если компилятор туда смотреть не будет — никакая виртуальность не заработает… Оттуда же можно было бы и про параметры узнать.
>Компилятор видит в этой строчке то, что ему предписано видеть. «Свободы воли» у него нет.

так написано то вызвать A::Foo :-)

>А почему бы и нет?

а потому что это не то поведение которое можно было бы ожидать от вызова

A::Foo с параметрами по умолчанию (ведь как описано выше, вызывается именно она)

>А откуда он знает что ему нужно вызывать B::Foo на самом деле?

компилятор не знает что ему надо вызывать B::Foo :)))) в строчке pa->Foo()

это знает программа в процессе выполнения :) когда программа дошла в процессе выполнения до этой строчки, так получилось что в pa оказался указатель на представитель класса B, взяв по типу класса VTable и получив ссылку на функцию программа доберется до функции B::Foo, которая у нас одна а не две (как вам бы хотелось)

так написано то вызвать A::Foo :-)
А вызвалась B::Foo! Куда файлить баг?

а потому что это не то поведение которое можно было бы ожидать от вызова
А почему нет?

A::Foo с параметрами по умолчанию (ведь как описано выше, вызывается именно она)
Выше написана чушь ибо вызывается не A::Foo, а B::Foo.

компилятор не знает что ему надо вызывать B::Foo :)))) в строчке pa->Foo()
Но он её как-то вызывает? Чудеса в решете.

когда программа дошла в процессе выполнения до этой строчки, так получилось что в pa оказался указатель на представитель класса B, взяв по типу класса VTable и получив ссылку на функцию программа доберется до функции B::Foo, которая у нас одна а не две (как вам бы хотелось)
И что это доказывает? Что вызов функций у нас виртуальный, а обработка параметров умолчания — невиртуальная? С какого перепугу так получилось и что было бы если бы разработчики чуток побольше подумали над этим феноменом?
>Но он её как-то вызывает? Чудеса в решете.

полное непонимание процесса, компилятор не вызвает функции вообще, они вызываются самой программой в процессе работы.

>Выше написана чушь ибо вызывается не A::Foo, а B::Foo.

у вас есть код:

E *e = create_some();
e->Foo()

какой метод вызывается в e->Foo()?

>И что это доказывает? Что вызов функций у нас виртуальный, а обработка параметров умолчания — невиртуальная? С какого перепугу так получилось и что было бы если бы разработчики чуток побольше подумали над этим феноменом?

и что такого? в с++ много чего есть невиртуального, и что? Тот же полиморфизм в с++ двоякий, как времени исполнения (виртуальность) так и шаблоны — времени компиляции. Вас это пугает?
полное непонимание процесса, компилятор не вызвает функции вообще, они вызываются самой программой в процессе работы.
Полное непонимание обсуждаемой проблемы.

у вас есть код:

E *e = create_some();
e->Foo()

какой метод вызывается в e->Foo()?
Вызывается E::Foo — ни о какой виртуальности не может быть и речи. Компилятор достаточно умён для того, чтобы просечь подобные вещи. А вот если вы поместите эти две строчки в разные функции — тогда вопрос становится интересным и в точке вызова e->Foo() придётся вставлять код, который выяснит — какую же именно функцию нужно вызвать.

и что такого? в с++ много чего есть невиртуального, и что? Тот же полиморфизм в с++ двоякий, как времени исполнения (виртуальность) так и шаблоны — времени компиляции. Вас это пугает?
Нет. Я просто хочу сказать что принятое решение создало крайне кривой и неудобный язык: нужно было либо запретить определение параметров умолчания в потомках, либо сделать чтобы и параметры вызывались тоже виртуально. То, что этого не сделали — косяк, каких в C++ много.

Вы же безуспешно пытаетесь доказать что если сделать определение дефолтных параметров виртуальным, то небо на землю рухнет или океан высохнет…
>Полное непонимание обсуждаемой проблемы.

с вашей стороны?

>Вызывается E::Foo — ни о какой виртуальности не может быть и речи. Компилятор достаточно умён для того, чтобы просечь подобные вещи. А вот если вы поместите эти две строчки в разные функции — тогда вопрос становится интересным и в точке вызова e->Foo() придётся вставлять код, который выяснит — какую же именно функцию нужно вызвать.

откуда компилятор видит что там генерируется в create_some()? эта функция вполне может быть себе библиотечной, и генерировать какого-нибудь потомка. И даже не надо эти строчки друг от друга разносить

>Нет. Я просто хочу сказать что принятое решение создало крайне кривой и неудобный язык: нужно было либо запретить определение параметров умолчания в потомках, либо сделать чтобы и параметры вызывались тоже виртуально. То, что этого не сделали — косяк, каких в C++ много.

для кого кривой и неудобный, для кого мощный и понятный. Опять же выше приводили пример с питоном, в котором поведение точно такоеже, считаете что и там дизайнеры языка ошиблись?

>Вы же безуспешно пытаетесь доказать что если сделать определение дефолтных параметров виртуальным, то небо на землю рухнет или океан высохнет…

нет, это вы пытаетесь объяснить что если сделать так как вы хотите то всем будет хорошо. Все объясняют вам что это не будет хорошо.
Опять же выше приводили пример с питоном, в котором поведение точно такоеже, считаете что и там дизайнеры языка ошиблись?
Нет, там дизайнеры сделали всё правильно, выводится
B.foo, n=20
Вcё «как у людей».
нет, это вы пытаетесь объяснить что если сделать так как вы хотите то всем будет хорошо. Все объясняют вам что это не будет хорошо.
Не вижу объяснений. Вижу всё более истеричные вопли с голословными заявлениями о том, что «будет плохо», «ой плооохо будет », «НУ ВАЩЕ — Я ЖЕ СКАЗАЛ ЧТО БУДЕТ ХРЕНОВО».

Сколько раз ни говори «халва» — во рту слаще не станет… Хотелось бы увидеть обещанные проблемы с «множественным наследованием, дочернее наследованием, обрезанием классов при присваивании не указателями и тп.»
пример с питоном не корректен :) pa там не имеет типа A* :-) а имеет сразу тип B :-)

спокойствие, только спокойствие (с) :)

давайте таки вернемся к вашему же примеру, цитирую: А как вы можете вообще быть в чём-то в этом случае уверены?

можете считать, что именно для уверенности и принята такое поведение. Как можно быть уверенным что передается туда по умолчанию с реализацией «отдельный потомок, отдельный параметр по умолчнанию»?
Как можно быть уверенным что передается туда по умолчанию с реализацией «отдельный потомок, отдельный параметр по умолчнанию»?
А зачем мне знать что туда передаётся именно этот параметр? На кой ляд? Что я получу от этого знания? В любом случае понять что эта функция делает я смогу только прочитав документацию.
а зачем вам тогда вообще туда параметры передавать? а зачем тогда вообще у этой функции параметр есть? :)
Ну например это может быть «кисть» для рисования объекта. Если у вас выбрана конкретная кисть — то её и будут использовать. А если нет — будет использоваться самая подходящая для данного типа объекта кисть (одна — для овала, другая — для прямоугольника).
опять же проблема реализации, в описанном вами примере вы зачемто перекладываете определение кисти на сам класс объекта. Такое решение страдает тем, что оно слабо расширяемо и сковано. Что будет если вы решите добавить новые кисти? Или переорганизовать кисти внутри кода программы? Лушее решение в данном случае — чтобы был некоторый класс предоставляющий интрефейс выбора кисти, параметром которому был бы контекст. Контекст в данном случае собственно объект который мы рисуем и текущие выбранные кисти.

опять же проблема реализации, в описанном вами примере вы зачемто перекладываете определение кисти на сам класс объекта.
Это логикой задачи определяется. Округлые линии лучше рисовать мягкой кистью, а треугольники и квадраты — жётской.

Лушее решение в данном случае — чтобы был некоторый класс предоставляющий интрефейс выбора кисти, параметром которому был бы контекст.
Класс, разумеется, есть. И умолчальным значением там стоит NULL. А если там NULL — то объект выбирает самую правильную кисть.

Этот пример — он почти из жизни. Вы таких функций можете найти в разных API миллион.
>Это логикой задачи определяется. Округлые линии лучше рисовать мягкой кистью, а треугольники и квадраты — жётской.

не суть :)

>Этот пример — он почти из жизни. Вы таких функций можете найти в разных API миллион.

то что эти API есть, не означает что это добро :) тем более не припомню таких API даже в том же MFC для виртуальных методов…
UFO just landed and posted this here
или какого потомка? :)) и с какими параметрами вызывать Foo? :-) с параметрами по умолчанию? с параметрами по умолчанию не от A::Foo?! а откуда компилятору знать какой там будет потомок? он видит только указатель на A, и вызов A::Foo именно поэтому и берет параметры по умолчанию от него. Ведь у нас одна функция а не две.
UFO just landed and posted this here
>Грубо говоря, адрес на метод храниться в переменной. Во время исполнения значение этой переменной может быть любой (что запишут, то и будет).

так значит во время исполнения выяснять какой там параметр, который был записан в программе статически? :-)

>программисту неважно, поэтому он эти параметры и не указал.

программисту не неважно, программист экономит своё время печати :), когда программисту чтото неважно он вообще пишет отвратительный код :)
UFO just landed and posted this here
я говорю, вы предлагаете то что записано в программе статически (вызывать Foo с параметром 10) вычислять в процессе работы программы? :)
UFO just landed and posted this here
параметр для функции указан? или у нас функция не принимает никаких параметров? параметр указан во время исполнения или известен еще на этапе компиляции? По моему очевидно что параметр известен на этапе компиляции. Вы же предлагаете реализацию, которая бы вычисляла параметр на этапе работы программы. Почему это не принято так, тут уже несколько человек вам объяснило несколькими способами.
UFO just landed and posted this here
>А по-моему это неочевидно.

думаю после этого обсуждения для вас стало очевидно что в с++ именно так как я описал? :)

>Та же путаница, что с вызовом конкретно A::foo() и A:foo() или потомка.

нет, эта путаница возникла только потому что вы подумали что в с++ именно так, а в с++ параметр подставляется на этапе компиляции…
Понимаете — у вас с TomTol разные понятия о том, что такое «очевидно». Для вас, я так понимаю, «очевидно» то, что следует из описания языка, для TolTol — «очевидно» то, что 90% народу (лучше, конечно 99%) даёт один и тот же ответ сходу.

Так вот история с дефолтными параметрами в C++ — ни разу не очевидна.
интересно, если вы возьмете произвольный язык D, и будете ожидать что в нем всё как в абсолютно другом языке V, много ли вы на нем напишите? :) каждый язык имеет свои ньюансы, странно было бы считать что язык это нечто такое мягкое и пластичное, как хочу так пишу, а не четкий набор правил…
UFO just landed and posted this here
UFO just landed and posted this here
полагаете я не изучал этот вопрос? :-)
опять же, если я не знаю чтото в стандарте языка, я просто это изучу а не буду говорить «блин что за уроды придумали язык, что за нелепейшая выдумка».
Коллега, давайте не будем передергивать! Я полагаю, что все активные участники этого обсуждения уже имели возможность изучить досконально все, что стандарт имеет сказать на эту тему.

Из моего исходного топика:

Для сомневающихся — §8.3.6, пункт 10 в текущем working paper стандарта ISO/IEC 14882: Programming Language C++. (Ссылка на working paper дана просто по причине его бесплатной доступности. В действующем стандарте 1998 года этот пункт находится в той же редакции.)
тут я не передергиваю, а отвечаю уважаемому TolTol, а не вам.

Вы удивились в статье почему это так, думаю вам ответили.
UFO just landed and posted this here
каша в голове как раз не у меня, судя по «неинтуитивности поведения с++» :)
А почему адрес вызываемой функции мы вычисляем динамически, а значение параметра — статически? Где логика?
ну для меня все логично :)

функция у вас таки с параметром, а не без параметра, вы его не указали, но указали компилятору директиву — для A::Foo параметр по умолчанию равен 10. Компилятор, в силу того что ему в момент компиляции доступен только вызов A::Foo по указателю pa подставляет туда именно это значение.

Еслибы у нас функция была бы без параметра, то компилятор вызывал именно ее — A::Foo(void) поидее именно так и должен был реализовывать код на языке С++ программист писавший эту запись. :-) (или вообще както подругому называть эту функцию, если действие которое выполняется с параметром по умолчанию четко предопределено)
Судя по тому что данный топик таки появился — можно воспринимать это описание далеко не только таким способом.
я согласен, что можно воспринять не так, но поэтому собственно топик и появился, чтобы обсудить почему это не так и как всё таки надо делать в этом случае.
Да — и до того момента пока не начались вопли про то, что это — единсвенный разумный подход это был разумный топик. Вот — очередная кривизна C++ и вот как с ней жить. Всё просто и логично.

Но стоило кому-то назвать вещи своими именами — и началась истерика.
кому кривизна, кому все просто и логично :) всё субъективно :) теже замыкания в JS много кто не сразу въезжает (а ктото и въехать не может вообще). Язык такой какой он есть, пользоваться или не пользоваться — решать каждому лично, но называть какойто язык кривым в силу непонимания — по моему неверно
называть какойто язык кривым в силу непонимания — по моему неверно
Я-то как раз прекрасно понимаю почему сделанно именно так или иначе. И зная историю C++ я понимаю что дефолтные параметры обрабатываются именно так как они обрабатываются не потому что так логичнее и удобнее, а потому что разработчикам первого компилятора так было удобнее его писать.

Сомнительно обоснование IMNSHO…
нет, именно потому что разработчики языка посидели подумали и приняли что именно они хотят, что хотели то и реализовали, а не то что хотите вы оглядываясь на какието другие изыскания.
программисту не неважно, программист экономит своё время печати :)
Почему вы решили что это единственный способ использования параметров по умолчанию? Потому что в C++ они больше ни на что не годятся?
можете считать и так :) если для вас язык с++ ограничен именно этим :) то что вы хотите реализовать прекрасно реализуется введением отдельных перегруженых функций программистом самостоятельно :)
Вообще любой алгоритм прекрасно реализуется на машине Тьюринга, так что это бредовый довод.
хватит уже вводить эмоции :) это не довод, это просто комментарий к вашему «ни на что не годятся» :) всё что вы хотите сделать в с++ можно сделать другими средствами. А параметры по умолчанию, они именно для этого: для короткой записи :)
А параметры по умолчанию, они именно для этого: для короткой записи :)
Это единственное для чего они годятся в сегодняшнем C++ — но почему это должно быть единственным что с ними можно сделать вообще?
мы про язык с++ говорим или про сферический язык в вакууме? :) в с++ оно именно так, потому что так хорошо для самого языка с++ :)
Язык — он не человек. Хорошо кому: пользователям или разработчикам компилятора?
пользователям с++, а не пользователям сферического коня в вакууме :) мы тут уже обсудили какие проблемы возникают когда человек пишет такие интерфейсные определения на с++ :)
Обсуждения не было — была истерика без единого осмысленного примера.
истерика с чьей стороны? я лично спокойно пытался донести «свет истины языка с++» (с) :))) могу ещё раз попытаться объяснить, но мне кажется вы уже всё поняли и теперь просто плывете по течению обсуждения :)
Вот у вас есть внешний класс который торчит из билиотеки через хедер файл.

class A
{
func(int n = 10);
}

внутри этой библиотеки вы нифига не знаете что твориться. Ну есть там 10-к или больше наследований от A, о которых вы не знаете ничего. Как вы будите воспринимать эту функцию? Может вам еще исходники либы, подарить? :)
Извиняюсь, ответ былы на пост khim.
Как вы будите воспринимать эту функцию?
Как функцию, которая принимает либо один аргумент, либо два. Не вижу тут ничего сложного.
нет она принимает либо один аргумент, либо ниодного, при этом поданное значение будет = 10.
Ну про количество аргументов я напутал, извиняюсь, конечно, но почему вдруг обязательно поданное значение должно быть равно 10? Если саму функцию можно переопределить (в вашем случае нельзя, ну да ладно — преположим что вы не забыли про virtual), то почему нельзя переопределить дефолтное значение?

На самом деле любое решение будет в том или ином случае неудобным потому я не уверен что дефолтные значения вообще имеют право на существование. У нас в коде они запрещены. В частности из-за описанных в статье чудес.
Переопределить дефолтное значение нельзя хотябы потому, что я как разработчик имеющий перед собой интерфейс класса, четко вижу что если я не подам аргумент функции то компилятор за меня туда подставит 10. Ну тут уже я не знаю как еще можно понимать. Вродибы есть интерфейс, вродибы в нем черным побелому написано что дефолтное значение =10, вродибы все однозначно.
Ну тут уже я не знаю как еще можно понимать. Вродибы есть интерфейс, вродибы в нем черным побелому написано что дефолтное значение =10, вродибы все однозначно.
С какого перепугу это дефолное значение вообще является частью интерфейса? Потому что так в C++ сделано? Плохо сделано. Переделывать уже поздно, но это не значит что нельзя было сделать лучше.
Мда… Тоесть по вашему правило «не верь глазам своим» это нормально в программировании да? Нужно запудрить программисту мозги как можно больше? Давайте будем писать в интерфейсах SetBgColor(Color color = RED); а сами в тихую в библиотеке отнаследуемся и пропишем SetBgColor(Color color = GREEN); Пускай разработчик порадуется когда будет втыкать в свой хедер файл и не понимать почему написано что у класса Window по дефолту вроде стоит фон крассный, а рисуется с хз чего зеленый… ЭТО НОРМАЛЬНО??? С++ ВИНОВАТ?

Ну тогда я уже не знаю…

А холиварить не хочу ибо всем известно чем это кончается.
Вы и так уже достаточно нахоливарили. Можете посмотреть как это сделано в динамических языках (тот же Python). Описанных вами проблем почему-то никто не наблюдает…
в том же питоне, что уже описывалось мной выше, мы по pa имеем сам объект B а не полиморфный указатель типа A* :-)
Правильно — Pyton даёт нам простые концепции, а С++ — даёт нам кучу кривых костылей. Но обязательно ли было этим костылям быть настолько кривыми? Нельзя ли было добиться эффективности с языком, от которого у программистов не так бы пухла голова? Ответ: наверняка можно — но пока этого никто не сделал…
см комментарий мой выше по этому поводу, думаю вы даже не знаете про текущие тренды с++ и чем именно он так цепляет современных разработчиков :)
Ну как вам сказать… Будет вам достаточно упомянуть о том, что я недавно участвовал в дискуссии о том, как исправить ошибку в кодогенераторе GCC 4.4 или нет?

Поверьте мне: я очень неплохо знаю C++ и что в нём бывает. Не на 100% (на 100% его не знает, я думаю, никто), но достаточно хорошо для того, чтобы сказать что в нём хорошо и что плохо.

Так вот стройностью этот язык никогда не отличался и «текущие тренды» в нём — это всё более-менее успешные попытки залатать старые дыры в дизайне…
а причем тут кодогенератор gcc? :-) и язык с++, кодогенератор к нему имеет очень опосредованное отношение :)

странно, вообщето с++ не меняется года этак с 97го :) в 2003 году стандарт обновляли, но там фактически только стандартную библиотеку расширили, никаких глобальных переделок :) такчто язык строен уже давным давно :-) а на дворе 2009й… :)

Тренды — это то что народ наконец то начал вытворять на этом языке, когда появились компиляторы реализующие почти 100% его функциональности.
странно, вообщето с++ не меняется года этак с 97го :) в 2003 году стандарт обновляли, но там фактически только стандартную библиотеку расширили, никаких глобальных переделок :) такчто язык строен уже давным давно :-) а на дворе 2009й
Вообще-то GCC с версии 4.3 поддерживает ключи -std=c++0x и -std=gnu++0x… Мне казалось вы о таких вещах как концепции (concepts), std::thread, auto и decltype… Всё это, конечно, хорошо, но настолько мало — за 10-то лет работы!

Тренды — это то что народ наконец то начал вытворять на этом языке, когда появились компиляторы реализующие почти 100% его функциональности.
В основном я вижу вещи, которые со страшным скрипом и ограничениями реализуют то, что на других языках делается легко и просто. И вызывает это скорее не восхищение, а жалость: ну неужели же нельзя было сделать язык чтобы всякие концепции реализовывались удобнее, чем этот кошмар? Чтобы нём была полноценная рефлексия и нормальные замыкания? Ну зачем людей-то мучить?
вы знаете что такое c++0x? :-)

это будущий стандарт с++, который, о Боги! :) является РАСШИРЕНИЕМ языка с++, то есть еще больше зубодробительных правил и тп :) это будущие тренды с++ :) а вы задумывались почему в с++ захотели добавить только это? :) всё очень просто, потомучто всё остальное прекрасно справляется со своими задачами. Даже то что добавляют в новый стандарт, фактически можно сделать текущими средствами :) (разработчики буста это преркасно показали :)

>В основном я вижу вещи, которые со страшным скрипом и ограничениями реализуют то, что на других языках делается легко и просто. И вызывает это скорее не восхищение, а жалость: ну неужели же нельзя было сделать язык чтобы всякие концепции реализовывались удобнее, чем этот кошмар? Чтобы нём была полноценная рефлексия и нормальные замыкания? Ну зачем людей-то мучить?

да? :-) операционная система Winodws это как раз тот пример который со скрипом реализуется на других языках? :) или есть еще какието примеры? :))
Даже то что добавляют в новый стандарт, фактически можно сделать текущими средствами :) (разработчики буста это преркасно показали :)
Вот это-то и ужасно. Вместо того, чтобы исправить болячки и добавить вещей, которых внутри самого языка не реализовать в него добавляют какие-то мелкие рюшечки и преподносят это как «грандиозный шаг вперёд»…

операционная система Winodws это как раз тот пример который со скрипом реализуется на других языках?
Так точно. Почти все фичи Windows были реализованы на Lisp-компьютерах много-много лет назад. То, что потратив буквально десятки тысяч человеко-лет и миллиарды долларов (то есть раз так в сто больше чем при работе на вменяемых языках) Microsoft смог «взять» эту, не бог весть какую высокую, высоту и превзойти её (уже в этом веке) — чести этому языку не делает. Это скорее доказывает что «и слона можно научить танцевать»…
>Вот это-то и ужасно. Вместо того, чтобы исправить болячки и добавить вещей, которых внутри самого языка не реализовать в него добавляют какие-то мелкие рюшечки и преподносят это как «грандиозный шаг вперёд»…

опять же, для кого болячки? для вас? или для программистов с++? :)

>Почти все фичи Windows были реализованы на Lisp-компьютерах много-много лет назад

а народ то и не знает :) :) :) :)

>это скорее доказывает что «и слона можно научить танцевать»…

это вы про лисп? :))
а народ то и не знает :) :) :) :)
Народ вообще много чего не знает.

это вы про лисп? :))
Ну разумеется! Когда вы свободно встраиваете любые объекты друг в друга как в Lisp'е — это не настоящий танец. Вот когда у вас куча компиляторов, костылей и сложных правил, работоспособность которых зависит от фазы луны (как в C++), тогда да — это то, что надо.

Вы тут что-то про MFC заливали. Вам самому не противно смотреть на всё это хозяйство и видеть что в дополнение к коду, который реально что-то делает, необходимо заполнять кучу всяких таблиц, что-то где-то регистрировать и т.д. и т.п. Да, Visual Studio умеет многое из этого делать за вас — но можно ли это приписать к достоинствам языка?
проблемы реализации (компиляторы) вы пытаетесь свалить на язык? не странно ли? давайте проблемы реализации борланда свалим на паскаль, проблемы реализации ruby свалим на сам язык, проблемы реализации c# на него же… и тп и тд

а в чем проблема встраивать объекты в друг-друга в с++? :)

я заявлял что в мфц описанного выше случая с параметром по умолчанию в виртуальном интерфейся я не наблюдал. Я ничего не говорил про его «достоинства». Моё мнение после работы с МФЦ — ахтунг, кто это вообще родил. Опять же в текущей своей деятельности я не под VS пишу, а всё больше на для gcc, поэтому за меня всё делаю я сам. Опять же, много знакомых с 5кой по брайнбенчу шпарят на с++ такие проекты… Объективная реальность такова, что конечный продукт, который будет работать на куче платформ, быстро, и не будет за собой тащить виртуальную машину, реально реализовывать только на с++
Вы разницу между языками Питон и С++ понимаете?
А пардон, уже выше написали :)
Отлично: Python — красивый, логичный, стройный язык, который, однако, не слишком эффективен, C++ — страшная «гремучая смесь» из кучи мразматических правил и криво понятых идей, для которой, однако, созданы весьма эффективные компиляторы.
во, как вы классно сказали «криво понятых», тут главное заметить кем криво-понятых :) криво понятых вами :) а не разработчиками языка :)
К огромному сожалению именно разработчиками. Типичная история такая:
— А вот в языке XYZ есть ABC! Давайте добавим в C++!
— Нет — это же приведёт к лишнему коду!
— А давайте тогда сделаем ABC'!
— Нет — ну так этим пользоваться будет нельзя!
— Ok, как насчёт A«bC?
— Неплохо, но теперь мы не можем сделать KLM и PQR…
— Ну PQR уже не спасти, давайте сделаем a!B!c…
— Годится.
Ну и последовательные итерации породили то, что породили. До 97го, примерно, года разработчики хотя бы не гнушались исправлять недостатки вводя несовместимости (operator new, постфиксные и префиксные operator++), но сейчас это уже приняло практически патологические формы: так как выкинуть никакую кривость нельзя (как же: совместимость — это теперь святое), то придумываются такие зубодробительные правила что сам чёрт ногу сломать может…
про описание «а вот в языке» :-) это точно не к с++ :) почитайте страуструпа «дизайн и эволюция языка с++» узнаете хоть как на самом деле оно происходило :)

в чём кривость то? в вашем непонимании? в вашем субъективном восприятии? тогда любой язык который вы не понимаете — кривой :)

вы не поверите, но во многих «простых» языках правила не менее зубодробительные. За примером далеко ходить не надо, почитайте драфт ECMAscript или тогоже Java :-)
почитайте страуструпа «дизайн и эволюция языка с++» узнаете хоть как на самом деле оно происходило :)
Знаете — это как учебник советской истории. Выделены удачные моменты, убраны неудачные, то что лично Страуструпу не нравится — совсем исключено.

в чём кривость то? в вашем непонимании? в вашем субъективном восприятии? тогда любой язык который вы не понимаете — кривой :)
Если бы я не понимал C++ — было бы не так обидно. Но там куча вещей, вызванных якобы стремлением к эффективности, которые потом боком выходят. Например те же невиртальные методы — зачем они? Концепция финальных методов (как в Java) — гораздо безопаснее и практически не менее удобна. Или typeid — это не рефлексия, а чёрт знает что. Ну и куча других моментов.

Я очень уважал C++ в 90е годы. Было видно что язык неидеален, но что его стремятся сделать лучше. Но то, что с ним произошло в XXI веке вызывает чувство горького разочарования.
>Знаете — это как учебник советской истории. Выделены удачные моменты, убраны неудачные, то что лично Страуструпу не нравится — совсем исключено.

вы сначала почитайте, прежде чем говорить :)

> Если бы я не понимал C++

но вы же сами признались что не понимаете его, сказав что поведение компилятора — кривизна! :-)

зачем невиртуальные методы?! а зачем вообще нужны методы?! :-) или класс без виртуальности уже не класс? :))

typeid ну тут уже как компилятор реализует :)

в 90е годы с++ фактически еще не было, нормальные компиляторы появились этого году после 2000го, а до этого это был «Си с классами», как не прискорбно этого говорить :(
но вы же сами признались что не понимаете его, сказав что поведение компилятора — кривизна! :-)
Маразм какой-то. Вы всеръёз верите в то, что понять что-то == полюбить это? Ну ладно — верьте дальше.

зачем невиртуальные методы?! а зачем вообще нужны методы?! :-) или класс без виртуальности уже не класс? :))
Вы даже не вдумались в то, что предлагается в качестве альтернативы — очень характерно для всего спора. Подумайте — чем финальные методы Java отличаются от невиртуальных методов C++, какие ограничения это накладывает и какие преимущества даёт — потом можно будет что-либо обсуждать.

typeid ну тут уже как компилятор реализует :)
Да проблема не в том, что он реализует, а в том чего он не рализует. Полноценной рефлексии он не релизует. Очень тяжёлая, сложная в реализации конструкция фактически даёт вам dynamic_cast — и всё.

в 90е годы с++ фактически еще не было, нормальные компиляторы появились этого году после 2000го, а до этого это был «Си с классами», как не прискорбно этого говорить :(
Правильно, но в 90е годы язык развивался, становился реально лучше и была надежда что «гадкий утёнок» превратится-таки в лебедя. А утёнок вырос и превратился в гадкого гуся — на чём развитие и застряло. Обидно.
>Маразм какой-то. Вы всеръёз верите в то, что понять что-то == полюбить это? Ну ладно — верьте дальше.

спокойствие только спокойствие, вы говорили про понимание, я указал что вы сами говорили про его не понимание, только и всего, причем тут любовь?

>Да проблема не в том, что он реализует, а в том чего он не рализует. Полноценной рефлексии он не релизует. Очень тяжёлая, сложная в реализации конструкция фактически даёт вам dynamic_cast — и всё.

а вы чего хотите от комипилируемого в машинный код языка? :)

>Правильно, но в 90е годы язык развивался, становился реально лучше и была надежда что «гадкий утёнок» превратится-таки в лебедя. А утёнок вырос и превратился в гадкого гуся — на чём развитие и застряло. Обидно.

формально можно считать что развивался язык только до 97 года, пока не был принят стандарт. Реально, всё что в стандарте было придумано страуструпом уже году к 95ому (бюрократия знаете ли), а компиляторы появились только году в 2001 нормальные, чувствуете сколько лет прошло? Гадкий утёнок — это ваше личное субъективное мнение, другие считают что вполне себе красивый лебедь вышел из гадкого утенка. Развитие не застряло, я профессионально программирую на нем с 97 года, то как приходилось программировать на нём тогда, и как можно (да и нужно) программировать на нём сейчас — небо и земля. А c++0x, только улучшит его.

я очень сильно подозреваю что вы даже не представляете что можно сейчас вытворять на этом языке…
спокойствие только спокойствие, вы говорили про понимание, я указал что вы сами говорили про его не понимание, только и всего, причем тут любовь?
Где я сказал что я чего-то не понимаю? Я прекрасно понимаю чем вызвано такое поведение (вначале сделали «как проще», а потом уже поменять что-либо не так просто), но понять и принять — разные вещи.

а вы чего хотите от комипилируемого в машинный код языка? :)
Посмотрите на Oberon, на Objective-C, на COM, наконец. Всё это — компилируемые языки. Почему ничего подобного нет в C++?

Реально, всё что в стандарте было придумано страуструпом уже году к 95ому (бюрократия знаете ли), а компиляторы появились только году в 2001 нормальные, чувствуете сколько лет прошло?
Чувствую. И в 1995м году стоило бы задуматься: а туда ли мы идём? Но нет — попёрлись через буреломы и тайгу…

очень сильно подозреваю что вы даже не представляете что можно сейчас вытворять на этом языке…
Очень хорошо представляю — ибо мне приходится всё это расхлёбывать. И ничего кроме недоумения большая часть этих достижений не вызывает: да, вы смогли наворотить 10'000 строк шаблонов и сделать что-то что можно было бы добавить в язык за неделю работы? Грандиозно — но нафига мне эта демонстрация RFC1925? Видимо глядя на один и тот же код вы вспоминаете первое предложение правила номер 3 и вы восхищаетесь его правдивостью, а я — второе и третье. Истинная мудрость этого правила — таки в третьем предложении, а C++-программистам свойственно его забывать…
>Где я сказал что я чего-то не понимаю? Я прекрасно понимаю чем вызвано такое поведение (вначале сделали «как проще», а потом уже поменять что-либо не так просто), но понять и принять — разные вещи.

нет, сделали так потому что так «правильнее», для вас это не кажется таковым, ну что же, это не проблема языка, а ваша личная проблема восприятия этого языка.

>Чувствую. И в 1995м году стоило бы задуматься: а туда ли мы идём? Но нет — попёрлись через буреломы и тайгу…

туда, ибо такой язык был нужен, и сейчас такого мощного компилируемого языка кроме с++ фактически нет. :-(

>…
в язык? что добавить в язык? примеры примеры, тут, извините, пустая болтовня. Что плохо реализуется текущим с++?
Ага… некоторые законы природы тоже кажутся такими коварными и зубодробительными что кажется что можно все сделать проще… Проще залезти например в пещеру и воспринимать мир в рамках этой пещеры, отбрасывая весь остальной мир как несуществующую реальность…
> До 97го, примерно, года разработчики хотя бы не гнушались исправлять недостатки вводя несовместимости
Да ну, совместимость там всегда была святое, я удивляюсь фоннатам цпп которые рекомендуют читать D&E — по-моему там каждая глава заканчивается или начинается словами «ну, по-человечески конечно было сделать нельзя чтоб ничего не сломать, поэтому пришлось сделать так..»
по вашему? может прочитаете таки? :) там такое только про синтаксис говорится :) до принятия стандарта 97 года руки у них были фактически развязаны куда двигать язык в плане фичастости
Ну я и говорю, удивляюсь, насколько все-таки разное у разных людей может быть восприятие одного и того же текста :)

Не только от книги, но и от статей и интервью страуструпа [у меня] создалось впечатление, что с первых лет они очень старались ничего не сломать, и во многом именно поэтому C++ получился таким кривым таким, какой он есть.

после стандарта, да, они старались и стараются, ничего не ломать, потомучто остутствие четкого и стабильного стандарта сделает язык непопулярным, так как разные платформы будут поддерживать его по разному. Стандарт обеспечивает заинтересованность в языке пользователей и реализаторов.
Да стандарт тут не причем в общем-то, язык задолго до 97 года уже был такой какой он есть (с компиляторами правда проблема была). Когда там шаблоны добавили — году в 90м?
шаблоны добавили в начале 90х но вот перестали изменять спецификации этих шаблонов, только незадолго до принятия стандарта…
Вобщемто Питон не компилируемый а интерпретируемый и почему у этих двух языков разная скорость работы, помоему понятно.

Ну вобщем что еще сказать… посмотрите на Lisp возможно после этого С++ вам покажется более менее стройным и понятным. Хотя конечно Lisp отличный язык безспорно :)
да уж луше сразу на брэйнфак смотреть :)
Lisp я немного знаю (к сожалению не довелось больших систем на нём писать) и, в частности поэтому, я лучше вижу всю убогость C++…
Ну тогда вы темболее должны понимать что List и С++ это совершенно разные языки. Это всеравно что говорить «китайский язык убогий, корявый и его придумали идиоты… ну не ужели не могли сразу русский придумать и говорить на нём»…
Lisp и С++ — это совершенно разные языки и я их даже пытаться сравнивать не хочу. И у каждого из них есть своя кривизна (да и вообще — идеальных языков не существует). Но сравнив многие конструкции C++ с их аналогами в тех языках, откуда они заимствованы становится грустно — ну за что с ними такое совершили? Почему в C++ методы принадлежат классу и могут определяться только первым, неявным, аргументом? Кто такое дурацкое ограничение придумал и зачем?
Знаете, за то слава богу — это работает и работает отлично в промышленных масштабах. Ну а правила… ну правила нужно учить, а как без этого…
Знаете, за то слава богу — это работает и работает отлично в промышленных масштабах.
Это такая практическая реализация ранее провозглашённого приниципа
Проще залезти например в пещеру и воспринимать мир в рамках этой пещеры, отбрасывая весь остальной мир как несуществующую реальность…
или как?

Multics (написанный на PL/I) дожил до 2000 года — я вот не уверен что какие-либо современные системы, написанные на C++ столько протянут…
протянут, была бы в них надобность :) вон в игрушки для zx написанных на АССЕМБЛЕРЕ(!!) до сих пор рубятся на эмуляторах :)

вас не устраивает логика языка? ну что поделать, язык значит не для вас. Пользуйтесь другим, никто не волит, но говорить что он убожествен только в силу собственного непонимания оного, както через чур, али нет? :)
Я что-то помнил про этот нюанс, но до него легко додуматься. Откуда ещё компилятор возьмёт 20, если у него указатель на базовый? Он же его нигде не хранит.
Так что ответ очевиден, хотя, возможно, не интуитивен.
UFO just landed and posted this here
Ну собственно там же Вам и ответили.
Вы предлагаете создать две функции вместо одной, это некорректно.
Затем я где-нибудь далеко в наследнике в другой dll создам функцию _без_ дефолтного параметра. И? К чему приведёт вызов foo()? Правильно, к вызову B::foo вместо моей.

Я понимаю, что принципиально можно многое и зачастую не так сложно, но речь о конкретном языке.
B::foo() разумеется, которая уже вызовет C::foo(30) со своим дефолтным параметром

Стоит так же не забывать о возможности брать указатель на виртуальную функцию.
UFO just landed and posted this here
Одной лишней виртуальной функцией можно поломать всю VTable.
И что? Если компилятор знает когда он добавляет функции, а когда нет — то проблем не будет.
UFO just landed and posted this here
Это синтаксический сахар.
Представьте такой код:
#include "A.h" // void foo(int x, int y = 10)

void foo(std::vector<A*> const & v)
{
v[0]->foo(5); // Уверен, что 10, это просто сахар
}

В противном случае придётся ручками писать v[0]->foo(5, 10).
И тут внимание! Вы скажете, что программист имел в виду, что y должен быть по дефолту, какой он там будет у наследника, а я скажу, что программист имел в виду, что y должен быть тот, что написан в A.h
Возможность переопределять дефолтный параметр сама по себе спорна.
Да и думаю, что если таки заняться копанием в стандарте, можно будет найти достаточно неочевидных случаев, но заниматься этим не хочется.

Приниципиально, конечно, многое возможно, достаточно посмотреть на более высокоуровневые языки. Но речь о Си++, и в его контексте я бы очень удивился замене одной функции на несколько реальных вне шаблонов.
UFO just landed and posted this here
сделано так не потому что спорно, а потому что параметры по умолчанию — директива компилятору, что вставлять в вызов функции когда он видит её вызов без параметров. Компилятор в описанном примере видит вызов функии A::Foo, вызов же функции B::Foo происходит уже во время работы программы без ведома компилятора.
Вы не заговаривайтесь. Если бы компилятор не использовал VTable, то никакого вызова функции B::Foo «без ведома компилятора» не произошло. Точно также и работу с дефолтными параметрами можно было через VTable сделать — никому от этого плохо бы не стало…
стало бы :) даже страуструп так считал (мои комментарии с цитатой из книги выше)
Вы так ни разу и не объяснили — кому стало бы хуже и почему. Все объяснения вращаются вокруг того, что делает компилятор — но никаких примеров чего-либо плохого (для компилятора или программиста) при противоположном подходе нет. Спасибо — мне не нужно объяснять как работает компилятор, я это и сам знаю. Объясните лучше в чём была бы проблема при подходе TolTol
стало бы хуже:
1) логическому восприятию:

есть интерфейс A::Foo( someClass x=SomeDefault() );
программист использующий интерфейс ОЖИДАЕТ ЧТО ЕСЛИ НЕ УКАЗЫВАТЬ НИЧЕГО БУДЕТ ПЕРЕДАВАТЬСЯ результат вызова SomeDefault() или вы воспринимаете это както иначе?

2) реализации и последующих проблем с оптимизациями
вы предлагаете хранить два (или больше, смотря сколько параметров по умолчанию) указателя в VTable, при компиляции генерировать также различные вызовы вместо одного. Генерировать отдельно столько же функций для всех потомков, ведь это по вашему отдельные функции. и тп

3) неоднозначности с множественными наследованиями и перемешиваниями
что делать если наследование вот такое вот, что делать если было обрезание что делать… ну и тп, только на первый взгляд сложности тут есть сразу с множественным наследованием
программист использующий интерфейс ОЖИДАЕТ ЧТО ЕСЛИ НЕ УКАЗЫВАТЬ НИЧЕГО БУДЕТ ПЕРЕДАВАТЬСЯ результат вызова SomeDefault() или вы воспринимаете это както иначе?
Я воспринимаю это как возможность использования функции с передачей параметра и без оной. Конкретное значение этого параметра — такой же факультатив, как и указание названий параметров.

вы предлагаете хранить два (или больше, смотря сколько параметров по умолчанию) указателя в VTable, при компиляции генерировать также различные вызовы вместо одного. Генерировать отдельно столько же функций для всех потомков, ведь это по вашему отдельные функции. и тп
Да, но зато мне не нужно генерировать код вызова SomeDefault() в каждой точке, где вызывается A::Foo — если вызовов много, можно получить изрядную экономию…

что делать если наследование вот такое вот, что делать если было обрезание что делать… ну и тп, только на первый взгляд сложности тут есть сразу с множественным наследованием
Нет там никаких сложностей. Ну то есть совсем нет. Простое правило тривиальным образом определяет что произойдёт в том или иным случае.
> Я воспринимаю это как возможность использования функции с передачей параметра и без оной. Конкретное значение этого параметра — такой же факультатив, как и указание названий параметров.

то есть этой функции наплевать на параметр вообще? :) зачем он тогда? :)
в общем случае это НАДО воспринимать именно как функцию с дефолтным параметром, а не без параметров. Для функции без параметров сигнатура была бы:

void A::Foo();

>Да, но зато мне не нужно генерировать код вызова SomeDefault() в каждой точке, где вызывается A::Foo — если вызовов много, можно получить изрядную экономию…

а куда этот код денется? :) экономии не получится вообще, экономия есть только когда функция одна, как собственно и есть в языке C++

>Нет там никаких сложностей. Ну то есть совсем нет. Простое правило тривиальным образом определяет что произойдёт в том или иным случае.

простое правило? :)))) уже тыщу раз объясняли что это простое правило усложняет и восприятие и реализацию :)
то есть этой функции наплевать на параметр вообще? :) зачем он тогда? :)
Ну зачем он ей нужен — конкретная реализация решит. А что он может присутствовать — полезно прописать в интерфейсе. В C++ же нет замыканий...
а куда этот код денется? :) экономии не получится вообще, экономия есть только когда функция одна, как собственно и есть в языке C++
Этот код окажется в той точке где реализована функция и если вы вызываете её больше одного раза — будет экономия. Когда же у вас есть «одна функция», то компилятор будет вставлять вызов конструктора во все вызовы функции Foo с дефолтным аргументом! Вот как раз в текущем подходе C++ никакой экономии нет — что функция с дефолтным параметром, что инлайновая функция без параметров плюс функция с параметром — код един.
простое правило? :)))) уже тыщу раз объясняли что это простое правило усложняет и восприятие и реализацию :)
Про усложнение реализации — это спорно, но возможно (выигрываем в одном месте, проигрываем в другом), про усложнение восприятия — только в том случае когда у вас мозги изуродованы существующим подходом C++.
>Ну зачем он ей нужен — конкретная реализация решит. А что он может присутствовать — полезно прописать в интерфейсе. В C++ же нет замыканий…

то есть таки параметр нужен? :)

вот вот замыканий нет :-) а вы пытаетесь их тут ввести в параметрах поумолчанию :)

>Этот код окажется в той точке где реализована функция и если вы вызываете её больше одного раза — будет экономия. Когда же у вас есть «одна функция», то компилятор будет вставлять вызов конструктора во все вызовы функции Foo с дефолтным аргументом! Вот как раз в текущем подходе C++ никакой экономии нет — что функция с дефолтным параметром, что инлайновая функция без параметров плюс функция с параметром — код един.

вы забываете что в вашем случае надо генерировать две отдельные функции foo(a) и foo(){ foo(default) }; а если параметров больше то функций становится еще больше!

>Про усложнение реализации — это спорно, но возможно (выигрываем в одном месте, проигрываем в другом), про усложнение восприятия — только в том случае когда у вас мозги изуродованы существующим подходом C++.

хватит уже эмоций :) большинство профессиональных программистов, именно на этом примере (когда интерфейсное описание пытаются переопределить в потомке) согласятся (и согласились в этом обсуждении) что та реализация что есть лучшая ввиду (повторюсь) простоты, непротиворечивости и невлиянии на интерфейс :)
большинство профессиональных программистов, именно на этом примере (когда интерфейсное описание пытаются переопределить в потомке) согласятся (и согласились в этом обсуждении) что та реализация что есть лучшая ввиду (повторюсь) простоты, непротиворечивости и невлиянии на интерфейс :)
Вы знаете — большинство Россиян согласятся с тем, что выучить русский несомненно проще, чем английский — что, однако не далает этот факт правдой.
ага, а большинство мозамбикцев, что мозамбикский :) и что? то что вы не знаете с++ не значит что его нельзя изучить и им пользоваться, причем эффективно :)
Я как раз знаю C++ — но чем больше меня пытаются убедить в его крутизне и показывают примеры оной крутизны, тем больше я вспоминаю про 3й пункт RFC1925.
5ка по брейнбенчу есть?
UFO just landed and posted this here
A a;

(a.*((void(A::*)(void))&A::x))();

Указатель на функцию-член класса. Компилируется и работает в VS2008, на других компиляторах не пробовал.

Знаете… Если я был бы компилятором — я бы сошёл с ума, разбирая это выражение. :-)
задача примитивнейшая, ниже вам вполне ответили, я извините спал в это время, вот вам поинтереснее, тоже с вызовом функций :)

class A{
template A(){ //do something };
}

как создать класс к конструктором по умолчанию типизированным int?
эхх тут хабр съел < >

template <typename D> A(){//do something};
UFO just landed and posted this here
UFO just landed and posted this here
ок, простой вопрос поставил вас в тупик :)

а ваш вопрос очень простой, опять же:

первая запись указывает компилятору вызвать метод в родителе, и эквивалентна A::x() без this-> (ведь мы итак нахоодимся внутри класса)

вторая запись означает вызов по указателю на функцию A::x для конкретного экземпляра на который ссылается this. По логике вещей, здесь должен вызываться метод x самого B, так как ссылка указывает на A::x, но применяется к классу B, где по адресу этого метода находится B::x

Проверка показала что для gcc я прав, вызывается сам B::x() в бесконечной рекурсии.
UFO just landed and posted this here
если вы клоните к моему объяснению поведения кода в начале обсуждения, то, повторюсь, оно давалось для смены неправильно понятия «интуинтивности», тех у кого в ответе на поставленый в статье вопрос получается 20.

P.S. теперь уже ваша очередь таки ответить на простой вопрос :)
UFO just landed and posted this here
я тоже не увидел вашего ответа, поэтому собственно и не отвечаю, хоть ответ и простой.
UFO just landed and posted this here
UFO just landed and posted this here
на тот вопрос я ответил сразу, и даже привел результат тестирования своего объяснения из gcc, на второй вопрос я уже сказал — имейте совесть и культуру общения, вам задали вопрос раньше, вы даже не потрудились на него ответить, почему я должен трудиться отвечать на ваши вопросы?
UFO just landed and posted this here
именно так и видно как я описал

да и ваш вопрос к значениям по умолчанию отношения не имеет :)
UFO just landed and posted this here
скомпилируйте в gcc и посмотрите свой смех на палке.

Хотя ко всем «специалистам» можно отнести стандартную практику поведения — не можем ответить на вопрос, игнорируем его, не можем аргументировать — оскорбляем.
UFO just landed and posted this here
точно также могу сказать «а причем тут MSVC?» логика работы именно такая как я сказал, какие функции создаются там компилятором себе в помощь — проблемы компилятора.

gcc в обоих случаях сгенерил идентичный код, отличающийся пустым mov eax,eax (видно оптимизатор сработал со вторым кодом)
UFO just landed and posted this here
совместима с обычными указателями? как это? я чтото не пойму какая тут может быть совместимость? Конвертировать можно один в другой? так стандарт с++ зарпещает преобразование указателей на функции к указателям на функции-члены и обратно. Размер имеют одинаковый? ну это уже вопрос реализации, каждый компилятор (на MVC свет клином не сошелся) имеет фактически своё строение этого указателя.

В чем совместимость вообще там может быть?!
UFO just landed and posted this here
кому «подпорка» вариант в mvc… опять же одинаковый размер указателей на функции члены — никак не регламентируется с++, полагаю каждый разработчик компилятора делал это исходя из эффективности реализации. mvc компилирует только для Win+x86 фактически, gcc — для огромного числа процессоров и платформ. Странно называть при таком неравном соотношении сил (не в пользу mvc) говорить так про gcc
UFO just landed and posted this here
msdn.microsoft.com/en-us/library/ms235435.aspx

итаниум там еще есть, вот я и говорю, что фактически только для x86Win

для WinCE идет сдк, со своим компилятором (раньше было по крайней мере так)
UFO just landed and posted this here
то есть таки отдельный компилятор? :))

самому не смешно?
UFO just landed and posted this here
на себя посмотрите, вопросы не я начал задавать, даже ответил на ваш вопрос, и даже проверил результат, вам не понравился компилятор, чтож это ваши личные половые трудности. Не нравится как работает ваш любимый компилятор — опять же ваши личные проблемы. Трудности с языком программирования — ну вы поняли…

а обзываться и наезжать может любой, так что сами догадайтесь кому тут надо успокоится
Плохо будет только программисту у которого есть только хедер файл с классом A и функцией f(int n = 10); И при этом объекты создаются через зашитую в lib файле фабрику.
В каком месте ему будет плохо и почему? Если для фабричного объекта лучше иметь умолчание не 10, скажем, а 100 — то почему ему будет плохо? А если ему нужно передать именно 10 и ничего больше — то почему этого не сделать явно?
Потомучто разработчику ничего об этом уже не известно! А расчитывает программист на то что будет 10!

Пишу пример

class Tank
{
void fire(int force = 10);
}

ну есть либа в которой есть скромная реализация

class SuperTank
{
void fire(int force = 1000);
}

толи по ошибке там написано, толи гдето внутри этой либо используется, я уже хз… извините. купил за 1000 баксов либу и мне наплевать что там внутри.

А вот на что мне не наплевать так на то что вызывая tank.fire(); вызов будет гарантированно той сигнатуре которая прописана в интерфейсе данного мне класса.
очевидно что из этого уже следует и то, что написанный год назад код с вызовом tank.fire() должен работать сегодня также как и год назад, учитывая что в интерфейсе ничего не поменялось. И уж точно никакой наследник не имеет право неявно менять интерфейс.
UFO just landed and posted this here
Нет уж извините, если интерфейс зафиксирован был год назад, то программист расчитывает на него. С танком конечно возможно не самый удачный пример, но думаю можно привести пример более жестких интерфейсов.
UFO just landed and posted this here
хм… ну тут помоему они по определению относятся к интерфейсу так как они в интерфейсе только и могут определяться, а не в реализации. Разве нет?
UFO just landed and posted this here
«Интерфейс (от лат. inter — между и лат. face — поверхность) — это семантическая и синтаксическая конструкция в коде программы, используемая для специфицирования услуг, предоставляемых классом или компонентом.» из википедии

Чему протеворечит в этом определении описание значения по умолчанию? :)
UFO just landed and posted this here
специфицируют что для данного класса и данной функции будет подставлено указанное значение по умолчанию в случае если не будет передано заданного параметра…

даже исходя из того что вы сказали «Интерфейс — это что-то общее.
А реализация — это уже конкретное.» следует, что указанное значение по умолчанию в интерфейсе имеет более общее значение нежели зависит от реализации.
UFO just landed and posted this here
Функция foo() это функция с совершенно другой сигнатурой. Как вы так хитро специфицировали интерфейс через реализацию другой функции?

А если это по сути

foo()
{
… Некие действия…
foo(100)
----некие действия
}
UFO just landed and posted this here
передать параметр 2. И вобще в такой ситуации компилятор выдаст ошибку ибо неоднозначность.
UFO just landed and posted this here
извиняюсь, про передать 2 лишнее…
вы просто никак это не вызовите, т.к. вы это не скомпиляете.
UFO just landed and posted this here
Ну вот у меня не собирается это под GCC… что у вас получается в таком случае?

Я к сожалению по этому поводу что пишет стандарт не помню. Но видимо раз у меня не компилируется, значит пишет что это неоднозначная ситуация и делать так нельзя.
UFO just landed and posted this here
class A
{
public:
void f(int a = 10)
{}
void f()
{}
};

A a;
a.f(); //error: call of overloaded 'f()' is ambiguous

UFO just landed and posted this here
пишу программисту класса A «убери дефолтный параметр из f(), у меня компилятор ругается»…

Ну а каких действий вы от меня еще ждали? :)
UFO just landed and posted this here
void foo int(a=10) — это описание одной функции с тем условием что если нет заданного аргумента он будет подставлен компилятором в месте вызова. Так написано в стандарте. Не прокакую подстановку её через другую функцию речи нет.

void foo() { foo(10); } — это объявление другой функции с её имплементацией. Извините, но когда я смотрю интерфейс то имплементация меня мало интересует… и то что эта функция подставит значение 10 по умолчанию никем не гарантируется. Я уже говорил, что реализация может меняться путем простой подстановкой dll. Не нужн вплетать в интерфейс реализацию другой функции.

неидеальность С++? А что в этом мире есть чтото идеальное? Я разве утверждаю что С++ идеальный язык? Боже упаси! С++ это язык и язык со своими законами. Не хотите следовать его законам не следуйте. Хотите чтобы программа работала то лучше следуйте законам языка С++. Но еще проще подыскать себе другой язык с другими законами.

А у меня всегда было желание написать свой язык, который будет удобен только мне и будет работать только по моим законам. Жалко времени нет…
В терминах С++ получается ничего более чем подстановка данного дефолтного значения в месте вызова данной функции, но никак не замена её на другую функцию.
Коллеги, у меня к вам несколько вопросов по результатам обширной дискуссии:)

Согласны ли вы, что следующее модифицированное определение класса:
class A {
public:
        virtual void Foo (int n);
        inline void Foo () {
                Foo (10);
        }
};


во всех смыслах эквивалентено исходному, использующему параметры по умолчанию:
class A {
public:
        virtual void Foo (int n = 10);
};


А именно, при вызове метода Foo с использованием параметра по умолчанию
A * pa = new A ();
pa->Foo ();

а) результат работы идентичен
б) не вызывает никаких побочных эффектов
в) не привносит сколь-нибудь существенного оверхеда
г) «интерфейс» класса A не претерпевает изменений

Я надеюсь, что здесь спорить не о чем, поэтому следующий вопрос. Изменяет ли следующая модификация класса A его «интерфейс»?
class A {
public:
        virtual void Foo (int n);
        void Foo ();
};


Надеюсь, что вы согласны с тем, что не изменяет.

Наверное, вы уже поняли, к чему я клоню. Хорошо видно, что константа 10 исчезла из «интерфейса» класса без вреда для него. Из чего следует, что она не является его частью. Из чего следует, что она является частью реализации класса A.
______________________
Текст подготовлен в Хабра Редакторе от © SoftCoder.ru
Нет не согласен. Во втором случае функция Foo() не является inline, а это ключевое слово является частью интерфейса объявленного ранее и указывает как компилятору, так и разработчику, что будет произведена точная подстановка указанной inline функции Foo (тело которой также можно отнести к интерфейсу поскольку оно неразрывно с ним связано). Я пологаю разница между этими двумя объявлениями интерфейса очевидна?
Помилосердствуйте, ну зачем же упорствовать!

Как может inline являться частью интерфейса, если это ключевое слово указывает компилятору исключительно на способ вызова функции?

Цитирую Вас: "… конструкция…, используемая для специфицирования услуг, предоставляемых классом..."

Услуг, а не подсказки компилятору о том, как можно более эффективно эти услуги получить.

В любом описании этого ключевого слова делается существенный упор на том, что его можно без каких-либо побочных эффектов опустить, компилятор также имеет право его проигнорировать.

Из этого же следует, что тело inline функции с ее декларацией никак не связано и может быть в абсолютно любой ситуации от нее оторвано и вынесено либо из класса с сохранением inline, либо вообще в другую единицу трансляции без сохранения inline.

Посмотрите на эту проблему и с другой стороны. Что такое интерфейс по существу? Это контракт между пользователем класса и его создателем. Изменится ли что-нибудь от наличия или отсутствия inline для пользователя класса, кроме, возможно, мизерной выгоды в эффективности, да и то совершенно эфемерной, ведь компилятор может ее проигнорировать.
Хорошо, давайте тогда уточним

Вопрос:

Являются ли данные объявления интерфейса эквивалентными:

1.
class A {
public:
virtual void Foo (int n = 10);
};

и

2.
class A {
public:
virtual void Foo (int n);
void Foo ();
};

Я отвечаю: Нет не являются, т.к.

вопервых: я как обладатель интерфейса, не знаю как себя ведут функции Foo() и Foo(int n).
во вторых: поведения этих двух функций может изменяться в зависимости от реализации и даже путем простой подстановкой другой dll или lib.
втретьих: это просто напросто у меня не компилируется :)

упс… ночь на дворе… туплю… :)

втретьих это я погорячился… компиляется конечноже, но сути сказанного это не меняет )
Поведение функций, естественно, зависит от реализации. Только если Вы уж начали говорить об «интерфейсах», нужно не забыть сказать, что клиент интерфейса имеет право исходить только из тех предположений о поведении класса, которые были зафиксированы в публичном контракте между создателем и пользоваталем.

С++ увы практически не имеет выразительных средств для специфицирования таких контрактов: преусловия, постусловия, инварианты и т. п.

Тем не менее, все, что Вы пытаетесь сделать, это убедить нас в том, что несмотря на это (отсутствие самых типовых механизмов для контрактов) в языке C++ есть один уникальный механизм для документирования одного очень специфического положения контракта класса: каким именно частным случаем является вызов некой функции без параметров по отношению к вызову фукции с тем же именем и с явно заданным параметром.

При отсутствии базовых механизмов создания контрактов, использовать такой уникальный механизм для одного единственного чрезвычайно специфического случая ну как-то странно, согласитесь!
Об интерфейсах начал говорить не я… но коли начали то начали. Все правильно, это публичный контракт.
С++ не имеет? С++ имеет и еще как, только понимаете в чем фишка, С++ это не совсем ООП в чистом виде язык… это мультипарадигменный язык. Поэтому все обсуждения нужно строить изходя из его мультипарадигменности. И боже упаси, ну не нужно его сравнивать с Java или Python и говорить что в С++ все плохо. В С++ все как раз хорошо, если кому и плохо то никого в мире С++ особо и не держат.

Боже упаси меня пытаться когото в чем-то тут убедить… Думаете мне это нужно? Вы спросили про эквивалентность интерфейсов. Я вам дал четкий ответ в терминах С++. Все! Меня не интересует обсуждение тогоже самого в терминах Java или другого «правильного» языка. Ну не интересно мне это… все эти обсуждения можно найти по адресу rsdn.ru в ветке холивар.

>> ну как-то странно, согласитесь!

Странно? Нет не странно. Не соглашусь. Почитайте литературу под названием «Мультипарадигменное проектирование в С++» и многие моменты кажущиеся вам странными моментально пропадут.

Все споры, как я понял лишь от того, что спорящие стороны исходят из какихто своих представлений о предмете спора. Я же все что пытался сделать, так это ответить на ваш вопрос в терминологии С++.

Ну вот не возникало у меня таких проблем которые описаны в этой статье. Возможно лишь потому, что я воспринимал дефолтное значение аргумента функции именно в терминологии С++ и только. Хотите писать правильно на языке следуйте его правилам и логике.

Плохой язык? хм… ну вот китайский мне тоже не нравится (сложно и много правил)… ну что поделать. Есть такой китайский язык и на нём говорят и многие очень успешно. А то что там понятия разные, так это уж извините, каждый язык несёт свои понятия и правила.

Как так вобщем…
С++ не имеет? С++ имеет и еще как


Покажите, пожалуйста, как на C++ оформить публичный контракт для класса. Заранее спасибо! :) Примечание. Про могучие фреймворки мы знаем, это уже не C++ получается, а «C++ с могучими фреймворками»

С++ это не совсем ООП в чистом виде язык… это мультипарадигменный язык.


Согласен. (Позволю себе пропустить Ваши предложения почитать о мультипарадигменности вообще и в C++ в частности, ибо читал достаточно много на эту тему.)

Тем более пропускаю Вашу попытку спровоцировать холивар, я обожаю C++ и люблю его (особенной любовью). Сравнивать языки (вернее конкретные родственные механизмы в разных языках) можно и нужно, ибо это помогает развивать мышление и кругозор. Вешать ярлыки «хороший» и «плохой» — никогда!

Теперь по существу вопроса.

Во-первых, принцип изоляции реализации от интерфейса отнюдь не исключительная прерогатива ООП. Принцип исключительно полезный и исходя именно из этого многие, если не большинство современных языков стремятся его так или иначе реализовать.

Поэтому рассуждения о контракте и интерфейсе в полной мере применимы к C++. Проблема только в том, что Вы смешиваете понятие «интерфейс», которое вообще говоря в C++ не определено, и понятие «определение класса», которое как раз принадлежит C++.

В том примере, который я привел выше, два определения класса A безусловно разные. Интерфейсы — идентичные. Даже несмотря на то, что число публичных методов одинаковое. С точки зрения интерфейса определение реализующего его класса — это уже деталь реализации.

И прошу заметить, что я здесь не пытаюсь описывать C++ в терминах другого языка. Только лишь в терминах универсального принципа изоляции интерфейса от реализации.

Еще раз, если вам все-таки хочется продолжать утверждать, что в мире C++ интерфейс класса и определение класса — одно и тоже, вспомните, что в C++ еще бывают публичные переменные-члены. Которые ну никак не сочетаются с принципом изоляции.

Необходимо признать, что данная концепция изоляции в C++ работает только через волевое признание воображаемой сущности «интерфейс» и неукоснительное следование определенному набору правил, обеспечивающему соблюдение той самой изоляции. Можно это сделать и с помощью хитрых программных фреймворков, но в этом случае все равно получится, что интерфейс <> определение класса.

Если же Вы отказываетесь признать понятие интерфейса в описаном выше смысле, то, тем самым, Вы просто признаетесь в том, что не можете последовательно придерживаться универсального принципа изоляции интерфейса от реализации. С++ действительно мультипарадигменный язык. На нем можно писать и так и сяк. Я люблю C++, но еще больше я люблю обсуждаемый выше принцип, поэтому я всегда буду писать на C++ с соблюдением данного принципа.

Именно поэтому для меня классы с одной функцией Foo (int n = 10) и с двумя Foo (int n) и Foo () обладают идентичными интерфейсами.
Более того исходный вариант и с inline это разные интерфейсы хотябы потому, что ключевое слово inline тебует, но всетаки оставляет на усмотрение компилятора подстановку тела функции. Поэтому прямой подстановки может и не быть. Тоесть это разные интерфейсы в любом случае.
UFO just landed and posted this here
UFO just landed and posted this here
Абсурд!

Последнее лишь утверждает что функция будет имплементирована в каждом объектом файле. А inline требует прямой подстановки тела функции в месте вызова.

Разве нет?
ну не будем четко говорить что «требует», а «просит» в будущей версии стандарта вообще хотят убрать inline. Современные компиляторы при включений оптимизаций сами все делают
Понимаете в чем проблема языка С++. Его сложно рассматривать в стороне от компиляторов. К примеру, есть стандарт с одной стороны и он действительно рекомендует компилятору вставить данное тело функции в место вызова. Но есть ситуации (например низкоуровнее программирование железяк), где компиляторы не такие умные как Visual C++ и там вся оптимизация ложится на плечи разработчика. Поэтому всетаки я допускаю случай когда inline требует подстановки в терминах определённой предметной области и компилятора.

Но по стандарту я погорячился. Действительно «просит» )
Они могут определяться только в интерфейсе исключительно за счёт того, что Страуструп так решил. Решил бы иначе — было бы иначе. Как-нибудь так:

class {
  int Foo(optional int);
};

int Foo(int x=10) {
  …
}

Ну или коротко:

class {
  int Foo(optional int) {
    …
  }
};

Сейчас же маразм: декларировать функцию можно хоть 100 раз, но параметры по умолчанию — только однажды. Это однозначно указывает на то, что в интерфейс засунули то, что должно бы быть в реализации: обычнаю декларацию можно повторять «без последствий»…
Я правильно понимаю что вы считаете что доверив тем самым людям, которые написали SuperTank::fire() право переопределять значение по умолчанию вы совершите криминал? Кто им запретит написать в первой же строке функции
if (force==10) force=1000;
Как вы вообще ожидаете влиять на поведение функции если вы не можете доверять её разработчикам?
Не подменяйте пожалуйста понятия. Речь идет не о доверии разработчикам а о доверии интерфейсам. И давайте не будем личные отношения между разработчиками выносить как предмет обсуждения. Речь идет о языке.
А что вам даёт это «доверие интерфейсам»? Вот какую практическую пользу вы из него можете извлечь?
поведение функии или её сигнатуру? :)
На кой ляд вам сигнатура без функции? Это уже какая-то Алиса получается с улыбкой без кота.
да у меня много таких :) тела функции нет, только сигнатура да документация по тому что будет если вызвать эту сигнатуру :)
документация по тому что будет если вызвать эту сигнатуру
То есть у вас всё-таки есть не только сигнатура?
только сигнатура, кода тела нет :)
А зачем вам «код тела»? У вас есть тушка, вы знаете что эта тушка делает — чего ещё нужно?
вот, я знаю, что в тушку передается параметр 10 по умолчанию, если я его не укажу, а не нечто, что там в коде наваял другой программер, кода которого я не вижу… Параметр по умолчанию же может быть не только числом, но и вообще любым классом.

к примеру:

void A::Foo(const D &b=D() ){… };
а в потомке, который нам не видно, объявляется другой инициализатор. Как вы думаете, будет ли программист смотрящий на этот интерфейс, ожидать что будет вызываться конструктор класса D если вызывать без параметров. Или должен догадываться из плясок с бубном, какой там конструктор будет вызыван и будет ли вызыван вообще…
Да вот с ходу пример. Есть у вас некий интерфейс к комуто API какойто сложной системы. И есть у вас куча апишных методов которые принимают всякие там флаги, как в Win32 API. Оооочень легко представить себе ситуацию когда программист в большинстве случаев будет использовать дефолтные значения параметров в большинстве случаев (как это бывает в Win32 API или MFC). И извините конечно, но мне глубоко накласть кто там и что от них внутри библиотеки имплементирует. Но я очень обижусь если мне потом саппорт будет говорить «Ну понимаете, мы тут подумали и решили поменять дефолтные значения в нашей библиотеке… вы там у себя перепишите весь код пожалуйста.»
Можете немного раскрыть пример? С какого перепугу они будут писать вам такое письмо и почему это же письмо не потребуется писать при том идиотском подходе, какой используется в C++?
Я привел это как пример того, что API прописанные 10 или 20 лет назад не меняются обычно серьезными компаниями… вот например я не переписывал еще ни одно приложение которое я писал под MFC 5 лет назад. Оно компиляется и сегодня с темже успехом в любой Visual Studio. И мне пришлось бы писать такое письмо в том случае еслибы интерфейсы MFC поменялись… ладно это уже оффтоп.

Не понял про идиотский подход в С++. Осветите пожалуйста?
Не понял про идиотский подход в С++. Осветите пожалуйста?
да уж тут светили, светили — всё без толку. С какого перепугу детали реализации (значения дефолтных аргументов) засунуты в C++ в интерфейс? Кому от этого хорошо?
вот вы и пришли к главному :) вы пытаетесь на нас перекинуть проблемы тех программистов которые придумывали такой интерфейс? :-)
Не поверишь… мне не плохо от этого. И той сотне программистов С++ которая сидит в моей конторе.

А проблемы описанной в топике у меня лично не возникало никогда.

Проще всего конечно кричать что С++ говно и все должно быть так как я решил а не страуструп… но это уже отдельная тема и выходит за рамки языков программирования :)
И той сотне программистов С++ которая сидит в моей конторе.
А той тысяче что сидит у нас — плохо и потому дефолтные аргументы запрещены. Теперь будем считать сколько у кого программистов?
сколько получает та тысяча что сидит у вас? и каков их уровень хотябы по томуже брэйнбенч? :)
Ну могу сказать только что неплохо получает — в разных странах по разному. А вот уровень по брейнбенч вряд ли удастся высчитать — как-то даже не видел статистики. По TopCoder'у считали — получалось неплохо. Выше многих стран…
5ка по брейнбенчу у многих?
UFO just landed and posted this here
не за один раз конечно, но даже в далеком 97ом я сдавал его на 4.5…
Вот что я Вам на это отвечу.

Когда Вам, как безусловно очень опытному разработчику (без какой-либо иронии), предложат должность технического директора в крупной компании, занимающейся разработкой, Вам, увы, придется изменить мнение насчет 5-ки по брейнбенчу у многих из тысяч программистов.

Гораздо конструктивнее (Вам как техдиректору) — пойти на поводу у большинства и сделать все возможное, чтобы их средние мозги делали меньше ошибок в коде.

Вот здесь и придется идти на компромисс с определением «интуитивности».
опять же, это проблема:
1) обучения
2) политики набора

но никак не языка с++
Абсолютно верно! Это не проблема С++.

Проблема С++ состоит в том, что он создает техническому руководству указанные Вами две проблемы :)

Простите меня, я ради каламбура, конечно, жутко утрирую :)
UFO just landed and posted this here
это в вашем случае это известно, в общем случае реализация потомка недоступна. Имеется только интерфейс.
Если вас неизвестна реализация то почему вы считаете что известное число 10 лучше подходит для этой реализации, чем неизвестное вам число, скрытое в недрах библиотеки?
это уже проблема того человека кто писал интерфейс :)
Я знаю что число 10 подходит именно мне а не реализации. И я готов не вбивать его каждый раз вручную, если в интерфейсе оно прописано по дефолту. Число 10 это например цвет в методе у класса Window::SetBgColor(Color = RED)… мне абсолютно наплевать что там больше и лучше подходит реализации, но я вижу четко своими глазами что если у окна я вызову метод SetBgColor без параметров то цвет фона будет крассный а не серобуромалиновый который может вдруг возникнуть в SomeStupidWindowImpl классе зашитом в Windows.lib версии 5.234234234
Не бойтесь — в SomeStupidWindowImpl цвет может поменяться и без всякого изменения параметров. Например если ваша программа будет внесена в список программ для которых при получении значения RED нужно рисовать серобуромалиновый фон.

Ещё раз: если вы не можете довериться описанию функции вообще, то какое дополнительное доверие вам даст тот факт, что на вход этой функции попадёт определённое вами число? На выходе-то всё равно может быть что угодно!
Вы понимаете что значит дефолтное значение флагов в API системы управления чемто очень большим и страшным (я не говорю про MFC или WinAPI)? и к чему может привести тот факт что некий «дебил» случайно переписал в реализации дефолтное значение только для того чтобы у себя в тестах данной реализации класса облегчить себе жизнь?
Если дебилов пускают в сложную систему, то такую систему уже ничто не спасёт — проверно на опыте.
Коварство C. Коварство ассемблера. Коварство машинного кода.
На то они и НЕ высокоуровневые. Нужно осознавать что делаешь. Неосознанный код на основе «рыбы» не всегда прокатывает.

А warning-ов или notice-ов для «C», действительно, не хватает.
if (var = 0) bla(); явно не блещет корректностью с логической точки зрения.
А всего лишь кнопка на клаве не сработала.
Тонну строк написал, все компилируется, а ошибку ищи свищи.
Для PC еще легко отладить, а для контроллеров или DSP тяжко.
в высокоуровневых всё ещё тяжелее :)
Некорректный пример. То, что вы написали, действительно бессмысленно.
А вот
if ( f = foo() )
  bar();
уже имеет смысл.

Но в любом случае на присваивания в таких местах компиляторы ругаются предупреждениями.
язык мой — враг мой Незнание — вон настоящий враг :)
(delete нужно дописать)
Мне кажется, ОС освободит всю выделенную программе память по её завершении. Разве нет?
смотря какая ОС :) по DOS с EMS менеджером памяти, надо таки освобождать :)
И часто Вы пишете программы под DOS с EMS?
щас не пишу, но у некоторых встраивамых систем к примеру так до сих пор и осталось. Кроме того, часто ли вам приходилось писать код используемый другими проектами? :) если часто, до освобождение ресурсов для вас — естественная вещь
многие переносят потом освобождение памяти ОС на другие ресурсы, к примеру на файлы. Откроют файл через fopen, запишут туда чтонибудь, а потом удивляются, почему в конечном файле ничего не записано. А они его не закрыли по выходу из программы. На ОС понадеялись, а ОС ничего не знает, о том что программа кэширует запись туда…
С такой позиции Вы несомненно правы.
Вы не переживайте, приходилось мне разный код писать. И я ни в коем случае не поддерживаю memory-leaks (ох, сколько крови они у меня выпили!), но в данном случае, наверное, можно этим и пренебречь, ибо пример служит иллюстрацией иной ситуации.

Хотя, с другой стороны, писать delete для new у автора должно было уже войти в привычку…
Может у автора привычка не писать delete нигде, а пользоваться всюду смарт поинтерами? Но в пример он их решил не тащить? ;)
Ну, смарт-поинтеры тоже не панацея…
There is no silver bullet конечно, но чем меньше в программе будет вызовов delete, тем лучше.
Не многие к сожалению знают про умные указатели из буста, а std::auto_ptr по-моему уж точно не silver bullet.

Хотя, в новом стандарте C++ смарт-поинтеры из буста войдут в STL, да?
Хотя, в новом стандарте C++ смарт-поинтеры из буста войдут в STL, да?
Ага, они уже в tr1.
Тут ответ одно слово: depends. На С++ и ядро можно пописать, и драйвера. Тем более может у вас ОС какая то странная (симбиан, или что либо ембеддед)
Про delete согласен полностью! Ну уж как-то так повелось, что стараешься ужать пример по максимуму, чтобы не отвлекаться от главного. Хотя раз уж написал return 0; то и delete pa; не грех написать.

Если брать любой код, который пишется для практического применения, то я из тех «пуристов», кто будет обливаться потом, но везде ставить смартпойнтеры или по-другому освобождать память. Такое уж воспитание, ничего с собой поделать не могу.
В любом случае, просто спасибо за статью. Не ново, зато интересно и ностальгическую слезу вызвало.

Articles