Есть шаблон функции. Он предназначен для работы с потомками некоторого базового класса. Реализовать его как просто функцию, работающую с базовым классом нельзя, так как он должен принимать и возвращать именно производный тип.
И хочется, чтобы этот шаблон не компилировался при попытке использовать его с классом не унаследованным от нашего базового класса, даже если у него есть все такие же функции, как и в базовом классе.
Пример:
class IReferenceCounted
{
public:
void grab();
bool drop();
};
// boost::intrusive_ptr при создании увеличивает счетчик ссылок на 1
// А класс IReferenceCounted при создании делает свой счетчик ссылок равным 1
// поэтому для корректной работы с IReferenceCounted нужно уменьшить счетчик ссылок сразу после созданияtemplate <typename T>
boost::intrusive_ptr<T> create_intrusive(T* t)
{
boost::intrusive_ptr<T> ptr(t);
t->drop();
return ptr;
}
В данном случае, функция create_intrusive должна принимать только указатель на объект производный от IReferenceCounted и возвращать умный указатель на него же. Но она не должна работать с другими типами, даже если у них есть функции grab и drop, так как у них скорей всего при создании счетчик ссылок будет равен нулю и функция не будет работать корректно.
Существует ли какой-то стандартный/правильный/простой и понятный способ сделать это?
АЕ>class IReferenceCounted
АЕ>{
АЕ>public:
АЕ> void grab();
АЕ> bool drop();
АЕ>};
АЕ>// boost::intrusive_ptr при создании увеличивает счетчик ссылок на 1
АЕ>// А класс IReferenceCounted при создании делает свой счетчик ссылок равным 1
АЕ>// поэтому для корректной работы с IReferenceCounted нужно уменьшить счетчик ссылок сразу после создания
АЕ>template <typename T>
АЕ>boost::intrusive_ptr<T> create_intrusive(T* t, typename boost::enable_if<boost::is_base_of<IReferenceCounted, T> >::type* = 0)
АЕ>{
АЕ> boost::intrusive_ptr<T> ptr(t);
t->>drop();
АЕ> return ptr;
АЕ>}
АЕ>
АЕ>Существует ли какой-то стандартный/правильный/простой и понятный способ сделать это?
enable_if и type_traits из boost. Смотри выделенное.
Здравствуйте, uzhas, Вы писали:
U>самый понятный имхо — это вставить static_assert внутри функции (довольно декларативно описываем ограничения на типы):
U>
Здравствуйте, wander, Вы писали:
W>Здравствуйте, Андрей Е, Вы писали:
АЕ>>Пример: АЕ>>
АЕ>>class IReferenceCounted
АЕ>>{
АЕ>>public:
АЕ>> void grab();
АЕ>> bool drop();
АЕ>>};
АЕ>>// boost::intrusive_ptr при создании увеличивает счетчик ссылок на 1
АЕ>>// А класс IReferenceCounted при создании делает свой счетчик ссылок равным 1
АЕ>>// поэтому для корректной работы с IReferenceCounted нужно уменьшить счетчик ссылок сразу после создания
АЕ>>template <typename T>
typename boost::enable_if<
boost::is_base_of<IReferenceCounted, T>
, boost::intrusive_ptr<T>
>::type
АЕ>>create_intrusive(T* t)
АЕ>>{
АЕ>> boost::intrusive_ptr<T> ptr(t);
t->>>drop();
АЕ>> return ptr;
АЕ>>}
АЕ>>
АЕ>>Существует ли какой-то стандартный/правильный/простой и понятный способ сделать это?
W>enable_if и type_traits из boost. Смотри выделенное.
лучше так. зачем иметь лишний рантайм параметр...
Здравствуйте, uzhas, Вы писали:
U>Здравствуйте, Андрей Е, Вы писали:
АЕ>>Существует ли какой-то стандартный/правильный/простой и понятный способ сделать это?
U>самый понятный имхо — это вставить static_assert внутри функции (довольно декларативно описываем ограничения на типы):
Но не самый оптимальный.
Имея static_assert внутри функции, при ошибке компилятор будет указывать на строку внутри функции, а не на вызывающую строку как с enable_if.
Здравствуйте, uzhas, Вы писали:
U>Здравствуйте, Андрей Е, Вы писали:
АЕ>>Существует ли какой-то стандартный/правильный/простой и понятный способ сделать это?
U>самый понятный имхо — это вставить static_assert внутри функции (довольно декларативно описываем ограничения на типы):
У static_assert и enable_if принципиально разная семантика.
static_assert не оказывает никакого влияния на поиск подходящей функции, т.е. если эту функция признана самой подходящей, будет выбрана они и после этого инстанцирована с ошибкой в static_assert (а не в месте вызова), даже если есть другая подходящая функция, для которой это ошибкой не будет. Плюс будет инстанцировано все тело функции целиком и ты получишь еще миллион ошибок оттуда. Это из минусов.
Соответственно static_assert подходит лишь тогда, когда есть ровно одна функция и никаких более вариантов. Из плюсов: не меняется сигнатура, мы точно знаем, какая именно функция не смогла инстанцироваться и какие именно условие не сработало.
enable_if работает иначе — функция просто удаляется из списка кандидатов при поиске функции, тело функции не будет инстанцироваться, а ошибка будет всего одна типа "функция не найдена" и будет указывать в место вызова. Плюс могут подойти и будут выбраны другие функции с меньшими ограничениями — это невозможно с static_assert (см. здесь, например: http://www.rsdn.ru/forum/cpp/3722136.1.aspx
). Это из плюсов.
Минус всего один — удаление функции происходит молча и ты не знаешь, какое именно условие не сработало. В результате частенлько бывает так, что у тебя есть десяток функций, защищенных разными enable_if, и ни одна не подходит — придется сидеть и разбираться, как так совпало (это не так страшно, но нужен навык).
АЕ>Во! То что нужно. Компактно, добавляется одна строчка. И понятно: из кода виден его смысл и предназначение.
АЕ>На текущий момент у меня сделано так:
U>>
Здравствуйте, Олег К., Вы писали:
ОК>Вот не понимаю. Какую-такую задачу ты решаешь что без темплейтов, static_assert-ов и прочих извратов ну совсем никак?
Это была попытка флейма? С каких это пор стандартные средства языка стали извратами?
ОК>>Вот не понимаю. Какую-такую задачу ты решаешь что без темплейтов, static_assert-ов и прочих извратов ну совсем никак?
J>Это была попытка флейма? С каких это пор стандартные средства языка стали извратами?
Нет. Это не попытка флейма. Я действительно не понимаю какие такие задачи решает большинство посетителей этого форума что им просто жизненно необходимо запихать в код как можно больше фич языка С++.
Исходя из собственного опыта, считаю что для того чтобы писать нормальный и понятный код нужно процентов тридцать языка. Ну а то что в плюсах есть куча фич — так это еще не значит что их непременно нужно использовать.
Здравствуйте, Олег К., Вы писали:
ОК>>>Вот не понимаю. Какую-такую задачу ты решаешь что без темплейтов, static_assert-ов и прочих извратов ну совсем никак?
J>>Это была попытка флейма? С каких это пор стандартные средства языка стали извратами?
ОК>Нет. Это не попытка флейма. Я действительно не понимаю какие такие задачи решает большинство посетителей этого форума что им просто жизненно необходимо запихать в код как можно больше фич языка С++.
задача у всех одна — написать прозрачный и безопасный код и переложить как можно больше проверок (а то и кодогенерации) на компилятор.
ОК>Исходя из собственного опыта, считаю что для того чтобы писать нормальный и понятный код нужно процентов тридцать языка. Ну а то что в плюсах есть куча фич — так это еще не значит что их непременно нужно использовать.
Знаешь, есть хорошее соображение насчет того, что в любом продукте 80% пользователей используют только 20% фич. Беда только в том, что у каждого пользователя эти 20% свои.
Аналогично, для каждой задачи может быть достаточно 30% языка, но у каждой задачи эти 30% будут свои.
Причем утверждение "как можно больше фич" не совсем верно, правильнее сказать так: "как можно больше безопасных высокоуровневых фич, чтоб не пользоваться опасными низкоуровневыми". Т.е. остаются все те же 30%, просто они другие — не в области жонглирования битами, указателями на void и прочими прелестями сишного подмножества. Не говоря уже о фиче "а это требование мы просто в комментарии перед функцией напишем".
Всегда, когда тебе нужно выразить в коде зависимость от типов, она наиболее прямолинейно решается шаблонами.
Иногда можно добиться результатов перегрузкой, но вот это в большинстве случаев действительно будет извратом (например, для перегрузки нужен по крайней мере объект соответствующего типа).
Тем более что и static_assert, и enable_if выражают условие максимально декларативно и пишутся в одну строчку.
ОК>>>>Вот не понимаю. Какую-такую задачу ты решаешь что без темплейтов, static_assert-ов и прочих извратов ну совсем никак?
J>>>Это была попытка флейма? С каких это пор стандартные средства языка стали извратами?
ОК>>Нет. Это не попытка флейма. Я действительно не понимаю какие такие задачи решает большинство посетителей этого форума что им просто жизненно необходимо запихать в код как можно больше фич языка С++.
J>задача у всех одна — написать прозрачный и безопасный код и переложить как можно больше проверок (а то и кодогенерации) на компилятор.
Это разве задача? Я о бизнес задаче, если что.
А те кто городят дополнительные проверки (а то и кодогенерацию), создают себе несуществующие сложности и упорно борются с ними. Делают все что угодно но только не решают поставленную задачу. В результате задача, которую можно было бы решить несколькими строчками кода, обрастает тоннами каких-то абсолютно ненужных проверок, темплейтов, перегруженных функций и операторов и т.д. и т.п.
ОК>>Исходя из собственного опыта, считаю что для того чтобы писать нормальный и понятный код нужно процентов тридцать языка. Ну а то что в плюсах есть куча фич — так это еще не значит что их непременно нужно использовать.
J>Знаешь, есть хорошее соображение насчет того, что в любом продукте 80% пользователей используют только 20% фич. Беда только в том, что у каждого пользователя эти 20% свои. J>Аналогично, для каждой задачи может быть достаточно 30% языка, но у каждой задачи эти 30% будут свои.
J>Причем утверждение "как можно больше фич" не совсем верно, правильнее сказать так: "как можно больше безопасных высокоуровневых фич, чтоб не пользоваться опасными низкоуровневыми". Т.е. остаются все те же 30%, просто они другие — не в области жонглирования битами, указателями на void и прочими прелестями сишного подмножества. Не говоря уже о фиче "а это требование мы просто в комментарии перед функцией напишем".
Вообще-то можно и нужно писать высокоуровневый код в котором просто не нужны никакие дополнительные проверки. Он и так будет по себе безопасен. А тут люди ставят себя в такие рамки и делают все что угодно но только не то что нужно.
И этот высокоуровневый код и будет содержать где-то процентов тридцать языка. Ты сам согласился с этим выше.
J>Всегда, когда тебе нужно выразить в коде зависимость от типов, она наиболее прямолинейно решается шаблонами.
Ну я знаю что решается шаблонами и могу тоже писать в стиле Александреску. Вопрос в другом: нужно ли это? Мой ответ — ни разу ни нужно. Какая-то подмена понятий здесь.
J>Иногда можно добиться результатов перегрузкой, но вот это в большинстве случаев действительно будет извратом (например, для перегрузки нужен по крайней мере объект соответствующего типа). J>Тем более что и static_assert, и enable_if выражают условие максимально декларативно и пишутся в одну строчку.
Зачем это вообще нужно? Ты не ставь себя в такие рамки чтобы нужны были эти ассерты и инэйблы и будет тебе счастье.
Здравствуйте, Олег К., Вы писали:
ОК>>>>>Вот не понимаю. Какую-такую задачу ты решаешь что без темплейтов, static_assert-ов и прочих извратов ну совсем никак?
J>>>>Это была попытка флейма? С каких это пор стандартные средства языка стали извратами?
ОК>>>Нет. Это не попытка флейма. Я действительно не понимаю какие такие задачи решает большинство посетителей этого форума что им просто жизненно необходимо запихать в код как можно больше фич языка С++.
J>>задача у всех одна — написать прозрачный и безопасный код и переложить как можно больше проверок (а то и кодогенерации) на компилятор.
ОК>Это разве задача? Я о бизнес задаче, если что.
А бизнес-задача вообще никак не связана с язком программирования, не говоря уже о каких-то его фичах. Если что.
J>>Причем утверждение "как можно больше фич" не совсем верно, правильнее сказать так: "как можно больше безопасных высокоуровневых фич, чтоб не пользоваться опасными низкоуровневыми". Т.е. остаются все те же 30%, просто они другие — не в области жонглирования битами, указателями на void и прочими прелестями сишного подмножества. Не говоря уже о фиче "а это требование мы просто в комментарии перед функцией напишем".
ОК>Вообще-то можно и нужно писать высокоуровневый код в котором просто не нужны никакие дополнительные проверки. Он и так будет по себе безопасен. А тут люди ставят себя в такие рамки и делают все что угодно но только не то что нужно. ОК>И этот высокоуровневый код и будет содержать где-то процентов тридцать языка. Ты сам согласился с этим выше.
Выше я говорил о высокоуровневых фичах языка, а не о коде, перечитай.
Высокоуровневый код строится на низкоуровневом коде, в котором все необходимые проверки будут. Вот этот низкоуровневый код надо как-то писать, правда?
J>>Всегда, когда тебе нужно выразить в коде зависимость от типов, она наиболее прямолинейно решается шаблонами.
ОК>Ну я знаю что решается шаблонами и могу тоже писать в стиле Александреску. Вопрос в другом: нужно ли это? Мой ответ — ни разу ни нужно. Какая-то подмена понятий здесь.
Замечательно, расскажи нам, как ты иначе выразишь в коде зависимость от типа. sizeof? dynamic_cast?
ОК>Зачем это вообще нужно? Ты не ставь себя в такие рамки чтобы нужны были эти ассерты и инэйблы и будет тебе счастье.
А мне и так счастье, но спасибо за заботу, конечно.
ОК>>>>>>Вот не понимаю. Какую-такую задачу ты решаешь что без темплейтов, static_assert-ов и прочих извратов ну совсем никак?
J>>>>>Это была попытка флейма? С каких это пор стандартные средства языка стали извратами?
ОК>>>>Нет. Это не попытка флейма. Я действительно не понимаю какие такие задачи решает большинство посетителей этого форума что им просто жизненно необходимо запихать в код как можно больше фич языка С++.
J>>>задача у всех одна — написать прозрачный и безопасный код и переложить как можно больше проверок (а то и кодогенерации) на компилятор.
ОК>>Это разве задача? Я о бизнес задаче, если что.
J>А бизнес-задача вообще никак не связана с язком программирования, не говоря уже о каких-то его фичах. Если что.
Ну вообще-то решаются бизнес задачи с использованием конкретного языка программирования. Вопрос в том, сколько нужно задействовать фич данного языка для решения данной задачи? В данном случае я не увидел ничего относящегося к бизнес задаче.
J>>>Причем утверждение "как можно больше фич" не совсем верно, правильнее сказать так: "как можно больше безопасных высокоуровневых фич, чтоб не пользоваться опасными низкоуровневыми". Т.е. остаются все те же 30%, просто они другие — не в области жонглирования битами, указателями на void и прочими прелестями сишного подмножества. Не говоря уже о фиче "а это требование мы просто в комментарии перед функцией напишем".
ОК>>Вообще-то можно и нужно писать высокоуровневый код в котором просто не нужны никакие дополнительные проверки. Он и так будет по себе безопасен. А тут люди ставят себя в такие рамки и делают все что угодно но только не то что нужно. ОК>>И этот высокоуровневый код и будет содержать где-то процентов тридцать языка. Ты сам согласился с этим выше.
J>Выше я говорил о высокоуровневых фичах языка, а не о коде, перечитай. J>Высокоуровневый код строится на низкоуровневом коде, в котором все необходимые проверки будут. Вот этот низкоуровневый код надо как-то писать, правда?
Низкоуровневый код это if-ы, switch-и и циклы. Весь остальной изврат с темплейтами — это от лукавого.
J>>>Всегда, когда тебе нужно выразить в коде зависимость от типов, она наиболее прямолинейно решается шаблонами.
ОК>>Ну я знаю что решается шаблонами и могу тоже писать в стиле Александреску. Вопрос в другом: нужно ли это? Мой ответ — ни разу ни нужно. Какая-то подмена понятий здесь.
J>Замечательно, расскажи нам, как ты иначе выразишь в коде зависимость от типа. sizeof? dynamic_cast?
Я вообще не вижу нужды выражать зависимость от типа, но коли вы ставите себя в такие рамки...
На счет dynamic_cast-a — я не помню когда я его использовал в последний раз, если вообще использовал, но если я буду видеть что без него никак (в чем я очень сомневаюсь), то я таки использую dynamic_cast.
ОК>>Зачем это вообще нужно? Ты не ставь себя в такие рамки чтобы нужны были эти ассерты и инэйблы и будет тебе счастье.
J>А мне и так счастье, но спасибо за заботу, конечно.
В общем, принцип KISS не является чертой С++ программистов, и этот форум тому подтверждение.
Как это сделать без шаблонов с if и switch ?
J>>>>Всегда, когда тебе нужно выразить в коде зависимость от типов, она наиболее прямолинейно решается шаблонами.
ОК>>>Ну я знаю что решается шаблонами и могу тоже писать в стиле Александреску. Вопрос в другом: нужно ли это? Мой ответ — ни разу ни нужно. Какая-то подмена понятий здесь.
А мне постоянно нужно.
Вот например, надоело каждый раз думать как вызывать find. Как метод или std::find.
Ведь для ассоциативных контейнеров вызов .find будет эфективней намного.