LINUX.ORG.RU

Facebook платит за устранение багов в реализации языка программирования D

 ,


1

5

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

Одно из определений языка D: «D — это то, чем должен был быть С++». Вокруг языка сломалось уже много копий, но несмотря на это язык продолжает жить и развиваться, демонстрируя свои замечательные возможности и расширяя свое сообщество. Все больше разработчиков из мира С++/Java пристально следят за развитием языка и стараются держать руку на пульсе. Должен отметить, что сообщество D не является ортодоксальным и фундаменталистким (что бы это ни значило), и нередко в ньюсгруппах можно увидеть, что в ответ на вопрос, можно ли использовать D для решения определенной задачи, члены сообщества рекомендуют задавшему вопрос использовать другой язык, отличный от D. Так что в лице сообщества D любой найдет грамотных специалистов своего дела, готовых ответить на нужный вопрос кратко и по существу. Все это делает развитие языка неизбежным и неотвратимым.

Список багов с ценами за их устранение

>>> Оригинал новости

★★

Проверено: Shaman007 ()
Последнее исправление: cetjs2 (всего исправлений: 6)
Ответ на: комментарий от anonymous

для нормального CTFE нужна нормальная же модульность и раздельная компиляция, с созданием символьной таблицы модуля во-первых; и нормальная рефлексия времени компиляции, во-вторых. как минимум, возможность перебрать все экспортированные функции/переменные модуля, узнать их типы, и т.п. typeinfo.

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

DarkEld3r ★★★★★
()
Ответ на: комментарий от anonymous

второе. когда есть неймспейсы нормальную модульность с нормальной раздельной компиляцией сделать гораздо сложнее

Ну тогда это «ничего особенного».

А неймспейсы вполне удобный способ обьединять логические сущности. Всё-таки пихать всё, что хотелось бы в одном неймспейсе иметь в один файл не здорово. Или как D тут предлагает поступать?

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

Ну «прекомпилированные хедеры» сами по себе костыль... Именно в плане удобства и скорости с преимуществами модулей не поспоришь.

А насчёт «физической структуры проекта» - она в С++ и так с логической вынужденно связана.

don't do that. циклические зависимости между модулями — туда же.

Циклические зависимости - понятно. А вот два файла с одинаковым именем не такая и редкая ситуация.

DarkEld3r ★★★★★
()
Ответ на: комментарий от anonymous

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

Ну нормальной рефлексии, конечно, нет. Тем не менее, есть type traits. Плюс побольше всего в бусте. Не так уж плохо, в общем. Хотя удобство, особенно написания таких вещей, а местами и использования оставляет желать лучшего.

например, про ограничения концепта if (isInputRange!(Unqual!Range));

В С++ это возможно (через SFINAE и iterator_category).

или проверить что есть поле типа hasLength

Возможно, как для полей, так и для функций.

проверить что компилируется код, рассчитанный на определённый template mixin (если нет, специализировать по-другому)

Ну миксинов, нет, в остальном SFINAE, еnable_if.

DarkEld3r ★★★★★
()
Ответ на: комментарий от anonymous

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

А следующий пункт (вернее его отсутствие) в D разве выполняется?

- наследование реализации через границы модулей.

DarkEld3r ★★★★★
()
Ответ на: комментарий от forCe

Можно посредством SFINAE руками.

Да, согласен. В следующем сообщение уже исправился.

DarkEld3r ★★★★★
()
Ответ на: комментарий от anonymous

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

Я так понял, что речь немного о другом. Вот скажем у меня на прошлом проекте все тесты проходили часов за шесть. Это на релизе. На дебаге дольше. Релизные тесты запускались каждый день, дебажные пореже. При этом они (дебажные) ещё ассерты проверяли (которые не такие как в Д, а которые можно (попытаться) проигнорировать).

Соответственно, если юнит-тесты в D для релиза запускать нельзя, то это не очень удобно.

DarkEld3r ★★★★★
()
Ответ на: комментарий от eao197

В ОО языке проверка на наличия поля в объекте не есть разумно. Разумнее проверять наличие метода. А это, вроде как, на SFINAE делалось в C++98/03.

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

DarkEld3r ★★★★★
()
Ответ на: комментарий от anonymous

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

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

опять же, весь вопрос адаптировать библиотеки.

Основная проблема, насколько я понимаю, как раз в том, что работать придётся (в том числе) и не с адаптированными. И с С библиотеками.

не думаю что это получится быстро — если некоторые и прекомпилированные хедеры ниасиливают,

Дык, модули наоборот упростить же должны это всё.

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

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

DarkEld3r ★★★★★
()
Ответ на: комментарий от anonymous

Например, вот Common Lisp — нормальный язык: единица компиляции это пакет, модуль; библиотека пакетов, система — единица компоновки как «разделяемая библиотека», а инклуды это вообще ad-hoc костыль и monkey patching, который всерьёз никто не воспринимает.

Я, конечно, не лиспер (несколько книг прочёл и немного побаловался и всё), но можно взглянуть, что пишут профессиональные лисперы про это:

Не нравится реализация пакетов (неймспейсов). Удобно иметь вложенные пакеты, но их нет. Возможность задания никнейма для пакета в самом пакете - членовредительская, ибо каждый гомо сапиенс для своей мегабиблиотеки задаёт двух-, трёхбуквенный никнейм. Букв в общепринятой латинице и так мало (опустим перечень адских пыток, которым после смерти будут подвергнуты те, кто пишет комментарии не на английском языке или вообще использует не-ASCII алфавит для идентификаторов), так ещё количество их кобминаций ограничено любовью человеков к акронимам и красивым сокращениям.

DarkEld3r ★★★★★
()
Ответ на: комментарий от eao197

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

Хм, а компилятор какой? Потому что в майкросовтовском это именно так. То есть даже следующий код компилируется:

template<typename T>
void f(T t)
{
    ololo 
}
Но это потому что они положили на стандарт, который разрешает «не проверять» только зависимые типы и т.д.

DarkEld3r ★★★★★
()
Ответ на: комментарий от DarkEld3r

Хм, а компилятор какой?

В последний раз я поймал это в MSVC++2010. Но, по-моему, аналогичные вещи случались и в GCC ранее.

eao197 ★★★★★
()
Ответ на: комментарий от anonymous

Хранение ссылки в поле объекта, кроме специальных случаев(локов, лямбд и пр.)чревато висячими ссылками...

Дык, с указателями точно такие же проблемы. Плюс дополнительные - непонятно надо удалять или нет плюс (проще) возможность неправильно инициализировать.

Ну и оператор= ломаем на ровном месте.

Не всегда он имеет смысл.

DarkEld3r ★★★★★
()
Ответ на: комментарий от eao197

В последний раз я поймал это в MSVC++2010. Но, по-моему, аналогичные вещи случались и в GCC ранее.

Они и в более поздних версиях ничего не поменяли. И насколько я знаю, даже не собираются. (:

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

DarkEld3r ★★★★★
()
Ответ на: комментарий от anonymous

Более, т.к. в случае указателя очевидно, что нужно следить за временем жизни.

С какой стати? Вот есть у меня кусок памяти в котором что-то лежит. Чтобы не бегать каждый раз по офсетам могу сохранить указатели на это что-то. Очевидно за временем жизни следить в случае указателя и ссылки нужно одинаково. То есть никак (ну кроме того, что не освободить основную память раньше времени).

В общем, это оправдано только для специальных случаев.

Которые не так уж редко возникают. У меня, по крайней мере.

DarkEld3r ★★★★★
()
Ответ на: комментарий от DarkEld3r

Дык, с указателями точно такие же проблемы.

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

Плюс дополнительные - непонятно надо удалять или нет

Как это «непонятно»? Это должно быть ясно из логики работы, комментариев и документации. В случае же ссылок вероятность «случайно» сохранить объект, который будет кем-то разрушен до разрушение твоего объекта возрастает.

class A
{
public:
    explicit A(const std::string &str);
...
};

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

forCe
()
Ответ на: комментарий от DarkEld3r

С какой стати?

Потому что ручное управление памятью требует четкого понимания, кто и за что отвечает.

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

Хорошо. Надеюсь, вы для этого не предлагаете использовать ссылки(хотя это допустимо, если речь идет о каком-то локальном блоке кода).

Очевидно за временем жизни следить в случае указателя и ссылки нужно одинаково.

Указатели - это самостоятельные объекты, значением которых являются адреса других объектов в памяти. Ссылка - это синоним/альтернативное имя объекта. Понимаете разницу?

forCe
()
Ответ на: комментарий от DarkEld3r

Которые не так уж редко возникают. У меня, по крайней мере.

Например?

forCe
()
Ответ на: комментарий от forCe

Потому что ручное управление памятью требует четкого понимания, кто и за что отвечает.

Чуть подробнее на эту тему: http://eao197.blogspot.com/2013/12/progc.html

Указатели - это самостоятельные объекты, значением которых являются адреса других объектов в памяти. Ссылка - это синоним/альтернативное имя объекта. Понимаете разницу?

Когда речь заходит о валидности _значения_ указателя и о _валидности_ значения ссылки нет никакой разницы. Указатели в C++ никак не усложняют возникновения повисших ссылок/указателей. Наоборот, способствуют подобным проблемам. Например, за счет возможности забыть написать вызов delete или же сделав повторный delete.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Когда речь заходит о валидности _значения_ указателя и о _валидности_ значения ссылки нет никакой разницы.

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

void foo(const my_type &obj);

void bar(const std::shared_ptr<const my_type> &obj_ptr);
void bar(const my_type *obj_ptr);

Неужели для тебя нет разницы? Я, например, воспринимаю foo, как функцию, принимающую объект по ссылке. Не более того.

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

С конструкторами все еще несколько запутаннее. Если я увижу

class A
{
public:
    explicit A(const my_type & obj);
...
};

То я предполагаю, что obj является аргументом конструктора, передаваемый по ссылке. Я буду крайне удивлен, если разработчик класса A решил сохранить его у себя. Ведь вот такие примеры вполне нормальны, но приведут к проблемам

A a(my_type(10, 20, "aaa"));

A foo()
{
    my_type x(10, 20, "aaa");
    return A(x);
}
И т.д.

Сравни, например, с

class A
{
public:
    explicit A(const std::shared_ptr<my_type> & obj);
...
};
Или даже просто с
class A
{
public:
    explicit A(const my_type * obj);
...
};

Я по крайней мере уже задумаюсь, зачем конструктор принимает указатель и кто должен отвечать за уничтожение объекта...

forCe
()
Ответ на: комментарий от eao197

Чуть подробнее на эту тему

Третий тип — это использование чужого объекта. Т.е. объект A ссылается на объект B, но никак не может повлиять на время жизни объекта B. О том, чтобы ссылка на B оставалась валидной в течении жизни объекта A должен позаботится тот, кто дает A ссылку на B.

Если объект A ссылается на объект B, но при этом не является его владельцем, то в рамках нового стандарта или, например, boost'а, самым логичным и безопасным решением является использование weak_ptr. Ну кроме специальных случаев, вроде std::lock_guard'а.

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

forCe
()
Ответ на: комментарий от forCe

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

Когда вызовы вложены, то «явный синтаксис» указателей становится не таким и явным.

void f(entity *some_value)
{
  // Много кода.
  // ...
  f2(some_value);
}
И что по второму вызову понятно? Да ничего, как и со ссылками.

Как это «непонятно»?

Да вот так и не понятно. Комментарии/документация абсолютно одинаково нужны и для ссылок и для указателей. А для «логики работы», в обоих случаях, надо код смотреть.

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

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

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

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

DarkEld3r ★★★★★
()
Ответ на: комментарий от forCe

Неужели для тебя нет разницы? Я, например, воспринимаю foo, как функцию, принимающую объект по ссылке. Не более того.

Разница есть, но не такая, на которую ты указываешь.

Что мешает реализовать функцию foo вот так:

void foo(const my_type & obj)
{
  some_global_variable = &obj;
}
?

Поскольку в C++ нет чистых функций вообще, тип параметра определяет только то, что можно сделать с самим параметром. Но ничего не говорит о том, что со значением параметра будет происходить внутри: будет ли он сохранен, передан в другой поток и т.д.

Так что в C++ всегда нужно знать, что будет делать функция/метод/конструктор с переданным ему значением. Появившиеся в C++11 unique_ptr и shared_ptr позволяют это чуть более явно декларировать. Но если по каким-то причинам unique_ptr и shared_ptr использоваться не могут (например, объект размещен не в динамической памяти), то ссылки и указатели оказываются одинаково уязвимыми к преждевременному уничтожению объекта, на который они указывают.

eao197 ★★★★★
()
Ответ на: комментарий от DarkEld3r

Когда вызовы вложены, то «явный синтаксис» указателей становится не таким и явным.

Он там явный, т.к. some_value - это указатель.

Да вот так и не понятно. Комментарии/документация абсолютно одинаково нужны и для ссылок и для указателей. А для «логики работы», в обоих случаях, надо код смотреть.

Если использовать ссылки, как вы предлагаете, то да. Но я такого не встречал в библиотеках(как в стандартных, так и в других). В реальных проектах попадалось, но достаточно редко.

forCe
()
Ответ на: комментарий от eao197

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

Уязвимы-то они одинаково, только предназначение у них совершенно разное. Ссылки не являются самостоятельными объектами, а вы предлагаете именно так их и использовать. Я же считаю, что для хранения нужно использовать умные указатели или, в определенных обстоятельствах, указатели сырые. Вам же не придет в голову делать ноду связного списка(даже если он иммутабелен) на ссылках? Или придет?

forCe
()
Ответ на: комментарий от eao197

например, объект размещен не в динамической памяти

умные указатели этого и не требуют.

forCe
()
Ответ на: комментарий от forCe

Указатели - это самостоятельные объекты, значением которых являются адреса других объектов в памяти. Ссылка - это синоним/альтернативное имя объекта. Понимаете разницу?

С++ не вчера увидел, не понимаю зачем такой тон. Разницу понимаю, только почему-то мы приходим к совершенно разным выводам. По моему, ссылка как минимум убирает часть вопросов - нет возможности переприсваивания и явно декларируется, что код получающий ссылку не будет удалять обьект. Почему такое «сужение ответственности» плохо мне не понятно.

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

DarkEld3r ★★★★★
()
Ответ на: комментарий от DarkEld3r

не понимаю зачем такой тон.

Если что, простите. И в мыслях не было как-то оскорбить=)

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

Это не просто сужение ответственности. Ссылки - это синонимы объектов, введенные в язык для перегрузки операторов. Они не являются «суженными» указателями, они по смыслу просто задают альтернативное имя объекту. Использовать их вместо указателей нужно везде, если это связано с передачей параметров, возвратом значения, локального синонима и пр. Но никак не для того, чтобы ссылаться из одного объекта на другой. Тут сырые-то указатели дурной тон, но они хотя бы намекают, что это не просто передача аргументов(прямое предназначение ссылок в С++).

forCe
()
Ответ на: комментарий от forCe

Но я такого не встречал в библиотеках(как в стандартных, так и в других).

std::lock_guard и ему подобные? Ты же сам этот пример приводил, вроде?

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

DarkEld3r ★★★★★
()
Ответ на: комментарий от forCe

> Что мешает реализовать функцию foo вот так

Здравый смысл.

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

void bar( const my_type * obj );
не будет вызывать delete для obj. Тогда как вы вполне допускаете, что это возможно.

> например, объект размещен не в динамической памяти

умные указатели этого и не требуют.

Отлично, тогда покажите, как в функцию с прототипом:

void bar( const std::shared_ptr<my_type> & obj );
передать объект, который был взят из пула объектов и должен быть возвращен туда же. Или как передать туда указатель на объект, размещенный во временной арене памяти, все объекты в которой будут одновременно разрушены вместе с ареной?

Кстати, раз речь зашла о пулах. Имеет ли право функция bar(const std::shared_ptr<my_type>&) сохранять где-нибудь переданный ей объект, если время жизни такого объекта определяется временем жизни его пула. И bar, при этом, ничего о пуле не знает.

Я же считаю, что для хранения нужно использовать умные указатели или, в определенных обстоятельствах, указатели сырые.

Не понятно, что вы понимаете под хранением. Если объект A должен отвечать за удаление B, то передавать B в A по ссылке неразумно, т.к. это запутывает пользователя. Если A на протяжении своей жизни пользуется B, то передача B в A по ссылке может быть безопаснее.

Вам же не придет в голову делать ноду связного списка(даже если он иммутабелен) на ссылках? Или придет?

Вы начинаете путать теплое с мягким. Ссылки не могут изменяться. Применять delete через ссылку на объект можно, но это средство запутывания программ. Поэтому строить динамические структуры данных на ссылках может быть затруднительно просто по техническим причинам.

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

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

typedef std::vector< std::string > str_vector_t;

class splitter_t
	: public std::unary_function< const std::string &, void >
{
	const std::string & m_value;
	str_vector_t & m_less;
	str_vector_t & m_equal;
	str_vector_t & m_greater;

public :
	splitter_t(
		const std::string & value,
		str_vector_t & less,
		str_vector_t & equal,
		str_vector_t & greater )
		:	m_value( value )
		,	m_less( less )
		,	m_equal( equal )
		,	m_greater( greater )
	{}

	result_type
	operator()( argument_type a )
	{
		if( a < m_value )
			m_less.push_back( a );
		else if( a == m_value )
			m_equal.push_back( a );
		else
			m_greater.push_back( a );
	}
};

И вот как этот функтор может использоваться:

str_vector_t src;
src.push_back( "1" );
src.push_back( "4" );
src.push_back( "3" );
src.push_back( "2" );

str_vector_t less;
str_vector_t equal;
str_vector_t greater;

std::for_each( src.begin(), src.end(),
		splitter_t( "2", less, equal, greater ) );

Легко можно увидеть, что строковый литерал «2» неявно преобразуется во временный объект типа std::string, ссылка на который сохраняется в конструкторе splitter_t и используется в splitter_t::operator().

Судя по вашим убеждениям здесь следовало бы писать как-то так:

const std::string value = "2";
std::for_each( src.begin(), src.end(),
		splitter_t( &value, &less, &equal, &greater ) );

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Отлично, тогда покажите, как в функцию с прототипом:

void bar( const std::shared_ptr<my_type> & obj );
передать объект, который был взят из пула объектов и должен быть возвращен туда же.

С помощью кастомного deleter'а.

Или как передать туда указатель на объект, размещенный во временной арене памяти, все объекты в которой будут одновременно разрушены вместе с ареной?

Зачем вам для этого shared_ptr? Тут нужны другие стратегии управления владением.

Кстати, раз речь зашла о пулах. Имеет ли право функция bar(const std::shared_ptr<my_type>&) сохранять где-нибудь переданный ей объект, если время жизни такого объекта определяется временем жизни его пула. И bar, при этом, ничего о пуле не знает.

Мне кажется, что вы понимаете предназначение shered_ptr. Он обеспечивает совместный доступ нескольких владельцев.

Если A на протяжении своей жизни пользуется B, то передача B в A по ссылке может быть безопаснее.

В специальных случаях, когда из кода будет всегда ясно, что вот такой-то объект A пользуется вот таким-то объектом B и что время жизни объекта B будет больше или равно времени жизни объекта A.

Кстати, пример, который вы показываете, вполне соответствует этому.

forCe
()
Ответ на: комментарий от forCe

> Отлично, тогда покажите, как в функцию с прототипом:

> void bar( const std::shared_ptr<my_type> & obj );

> передать объект, который был взят из пула объектов и должен быть возвращен туда же.

С помощью кастомного deleter'а.

В вашем прототипе нет кастомного deleter-а. Вы прибили к своей реализации bar политику владения obj гвоздями.

eao197 ★★★★★
()
Ответ на: комментарий от forCe

Что? Кастомный deleter передается в конструкторе.

Да, тут моя ошибка, перепутал с одной из нестандартных реализаций shared_ptr.

Тем не менее, имеет ли функция bar(const std::shared_ptr<my_type>& obj) сохранять или передавать куда-нибудь полученный obj?

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Да, имеет. Через shared_ptr. Но, повторюсь, если у вас стратегия управления временем жизни, отличная от концепции shared_ptr, то он вам, естественно, не подходит.

forCe
()
Ответ на: комментарий от forCe

Да, имеет. Через shared_ptr.

Если shared_ptr с кастомным deleter-ом используется для возврата объекта в какой-то пул, то время жизни объекта начинает определяться не только shared_ptr, но и временем жизни пула.

Т.е. bar может хотеть что-нибудь, но тот, кто ее вызывает должен понимать, что bar хочет и можно ли это обеспечить.

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

Речь же не об этом, а о том, что у указателей, по вашим словам, шансов остаться валидными больше шансов, чем у ссылок. Я же защищаю другую точку зрения: нет никакой разницы. Зато у ссылок больше ограничений, поэтому их использование предпочтительнее.

eao197 ★★★★★
()
Последнее исправление: eao197 (всего исправлений: 1)
Ответ на: комментарий от eao197

то время жизни объекта начинает определяться не только shared_ptr, но и временем жизни пула.

В этом случае, вы неправильно используете shared_ptr=)

Т.е. bar может хотеть что-нибудь, но тот, кто ее вызывает должен понимать, что bar хочет и можно ли это обеспечить.

Я вас не понимаю. Само использование shared_ptr об этом говорит.

Речь же не об этом, а о том, что у указателей, по вашим словам, шансов остаться валидными больше шансов, чем у ссылок

Не совсем. Я просто хотел сказать, что при использовании ссылок сохранение ссылки где-то еще выглядит неочевидно, за исключением специальных случаев. Происходит это потому, что наиболее очевидное использование ссылок - это передача параметров/возврат по ссылке. Собственно, для этого они в язык и были введены.

forCe
()
Ответ на: комментарий от forCe

Я просто хотел сказать, что при использовании ссылок сохранение ссылки где-то еще выглядит неочевидно, за исключением специальных случаев. Происходит это потому, что наиболее очевидное использование ссылок - это передача параметров/возврат по ссылке.

Ну а я пытаюсь показать, что это лишь особенности вашего субьективного восприятия ссылок. А объективно, если отбросить «выглядит неочевидно» и «наиболее очевидное», голые указатели не дают никаких дополнительных гарантий. Даже с умными указателями могут быть фокусы.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Ну а я пытаюсь показать, что это лишь особенности вашего субьективного восприятия ссылок.

Нет, это у вас какое-то субъективное восприятие. Ссылки введены для передачи/возврата значений. Поскольку без них нельзя было сделать перегрузку операторов. Об этом написано в D&E.

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

голые указатели не дают никаких дополнительных гарантий

Нет, они лишь показывают клиентам, что это не просто передача параметра в конструктор.

Даже с умными указателями могут быть фокусы.

Если их неправильно использовать.

Кстати, про weak_ptr вы мне так и не ответили. А ведь это действительно один из самых безопасных способов ..эм.. «долговременного» хранение «ссылки» на объект, время жизни которого контролирует кто-то другой.

Так что подход с ref в D несколько логичнее ссылок в C++.

forCe
()
Ответ на: комментарий от eao197

Кстати, сейчас посмотрел=)

Не только D&E подтверждает мои слова.

В «The С++ Programming Language» Старуструп говорит о ссылках, как об альтернативном имени для объекта, пишет, что ссылки не являются объектом, рассказывает о передаче аргументов/возврате по ссылке.

В «Programming - Principles and Practice Using C++» он вообще начинает говорить о ссылках именно в контексте разговора о функциях и передачи аргументов по ссылке и возврате ссылки. И так же рассматривает ссылки как синонимы.

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

forCe
()
Ответ на: комментарий от forCe

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

Вот именно. Контекст и правила их использования сильно ограничены, этим-то они и привлекательны.

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

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

* компилятор бьет по рукам если вы забыли ее проинициализировать;

* ссылка не может поменять свое значение так же просто, как указатель;

* к ссылке нельзя случайно применить delete.

Так что не суть важно уже зачем ссылки добавлялись в язык тридцать лет назад. За это время для них нашлось дополнительное применение.

Нет, они лишь показывают клиентам, что это не просто передача параметра в конструктор.

Так а если вам нужно передать ссылку на объект в конструктор для того, чтобы в рамках ограниченного скоупа можно было ссылаться на этот объект? Зачем здесь указатель? Чтобы забыть его проинициализировать в конструкторе? Вызвать затем у пользователя вопросы об отсутствии delete в деструкторе? А так же о том, подойдет ли в этом случае для объекта сгенерированный по умолчанию оператор копирования? Ссылки в данном случае все эти вопросы просто снимают.

Кстати, про weak_ptr вы мне так и не ответили.

Не ответил, поскольку никогда ими не пользовался. Не приходилось мне иметь дело с такими хитрыми графовыми структурами данных, где нужно было бы иметь жесткие и мягкие ссылки на объекты.

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

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

Из-за этого любой голый указатель, любая операция new автоматически привлекает к себе максимум внимания.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Контекст и правила их использования сильно ограничены, этим-то они и привлекательны.

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

Так а если вам нужно передать ссылку на объект в конструктор для того, чтобы в рамках ограниченного скоупа можно было ссылаться на этот объект?

Это и есть особые случаи=) С этим-то я согласен.

Не ответил, поскольку никогда ими не пользовался. Не приходилось мне иметь дело с такими хитрыми графовыми структурами данных, где нужно было бы иметь жесткие и мягкие ссылки на объекты.

Так вы в блоге говорите об

Третий тип — это использование чужого объекта. Т.е. объект A ссылается на объект B, но никак не может повлиять на время жизни объекта B. О том, чтобы ссылка на B оставалась валидной в течении жизни объекта A должен позаботится тот, кто дает A ссылку на B.

weak_ptr позволит сделать это безопасно. В отличие от ссылок(которые могут запутать людей) и сырых указателей.

class A
{
public:
    explicit A(const std::shared_ptr<T> & obj)
        : weak_ref(obj)
    {
    }

    //...

    void foo()
    {
        if (auto obj = weak_ref.lock()) {
            // ...
        }
    }

    //...

private:
    std::weak_ptr<T> weak_ref;
};
forCe
()
Ответ на: комментарий от forCe

weak_ptr и shared_ptr накладывают ограничения на то, как должен быть создан объект, на который идет ссылка. Плюс, любое использование shared_ptr/weak_ptr — это атомарные операции над счетчиками ссылок. Плюс дополнительное выделение памяти под эти самые счетчики ссылок.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

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

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

forCe
()
Ответ на: комментарий от eao197

любое использование shared_ptr/weak_ptr — это атомарные операции над счетчиками ссылок.

Не любое. Только копирование.

forCe
()
Ответ на: комментарий от eao197

Что мешает реализовать функцию foo вот так:

void foo(const my_type & obj)
{
  some_global_variable = &obj;
}

мешает забота о клиенте — т.е. нежелание, чтобы клиент случайно напоролся на багофичу «удержания объекта по константной ссылке»

рассмотрим оба варианта:

#include <iostream>

class MyType {
public:
  enum Status { LIVE, DEAD };
  MyType() : value(LIVE) {}
  ~MyType() { value=DEAD; }
  void say() const { std::cout << ( value==LIVE ? "live\n" : "dead\n" ); }
private:
  Status value;
};

const MyType* some_global_variable;

#ifdef REF
void foo(const MyType& obj)
{
  some_global_variable = &obj;
  some_global_variable->say();
}
void client()
{
  foo(MyType());
}
#endif
#ifdef PTR
void foo(const MyType* obj)
{
  some_global_variable = obj;
  some_global_variable->say();
}
void client()
{
  foo(&MyType());
}
#endif

int main()
{
  client();
  some_global_variable->say();
  return 0;
}

$ g++ -DREF const_ref_bug2.cxx && ./a.out 
live
dead
$ g++ -DPTR const_ref_bug2.cxx && ./a.out 
const_ref_bug2.cxx: In function ‘void client()’:
const_ref_bug2.cxx:34: warning: taking address of temporary
live
dead
$

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

www_linux_org_ru ★★★★★
()
Ответ на: комментарий от eao197

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

error: invalid initialization of non-const reference of type ‘MyType&’ from a temporary of type ‘MyType’

имеем не предупреждение, а ошибку; однако цена, которую за это надо заплатить — убрать слово const — по-моему, непомерна

www_linux_org_ru ★★★★★
()
Ответ на: комментарий от forCe

Что мешает реализовать функцию foo вот так

Здравый смысл.

вообще-то здравый смысл тут говорит как раз *наоборот*

а реализовать так функцию мешает багофича с++ «удержание константных ссылок» и забота о клиенте: Facebook платит за устранение багов в реализации языка программирования D (комментарий)

www_linux_org_ru ★★★★★
()
Последнее исправление: www_linux_org_ru (всего исправлений: 1)
Ответ на: комментарий от eao197

я думаю, что в с++ слишком мало видов указателессылок, чтобы можно было им универсально присвоить различную непротиворечивую семантику; да, для каких-то ситуаций явно видно, что один_вид_указателессылок явно лучше другого_вида_указателессылок, а в других ситуациях — беспорядок

www_linux_org_ru ★★★★★
()
Последнее исправление: www_linux_org_ru (всего исправлений: 1)
Ответ на: комментарий от www_linux_org_ru

Ты сам-то как считаешь, должны быть ссылки типами, как в С++, и, соответственно, допускаться в полях объектов, или же, как в D, ссылки должны быть ..эм.. способом обращения, передачи параметров и возврата?

forCe
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.