Почему именованные параметры используются реже?

Я разработал класс параметров, который позволяет мне писать такой код:

//define parameter
typedef basic_config_param<std::string> name;

void test(config_param param) {

  if(param.has<name>()) { //by name
    cout << "Your name is: " << param.get<name>() << endl;
  }

  unsigned long & n = param<ref<unsigned long> >(); //by type
  if(param.get<value<bool> >(true)) { //return true if not found
    ++n;
  }
}


unsigned long num = 0;
test(( name("Special :-)"), ref<unsigned long>(num) )); //easy to add a number parameter
cout << "Number is: " << num; //prints 1

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

Почему этот синтаксис не используется чаще, перегружая operator,() для реализации именованных параметров? Это из-за потенциального снижения производительности?

Еще один способ - использовать названную идиому:

object.name("my name").ref(num); //every object method returns a reference to itself, allow object chaining.

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

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


person Fredrik    schedule 05.04.2012    source источник
comment
... потому что это добавляет сложности и просто не полезно?   -  person Ed S.    schedule 06.04.2012
comment
Что это дает?   -  person Oliver Charlesworth    schedule 06.04.2012
comment
Кроме того, перегрузка operator, обычно плохая идея, потому что она не будет вести себя как обычный оператор , (похожая логика объясняет, почему не следует перегружать operator&& или operator||).   -  person Oliver Charlesworth    schedule 06.04.2012
comment
Да, я имею в виду, что я вижу здесь много сложностей, а пользы почти нет. Запомнить; сложность стоит денег. Людям требуется время, чтобы понять, что происходит ($$$), и требуется время, чтобы поддерживать/исправлять связанные с этим ошибки (опять же, $$$). Честно говоря, если бы я увидел что-то подобное в коде, над которым работаю, мне пришлось бы поболтать с тем, кто это реализовывал. Для меня это попахивает чем-то, что придумал бы неопытный разработчик.   -  person Ed S.    schedule 06.04.2012
comment
Эта функциональность уже много лет доступна в Boost: boost.org/doc/libs/1_49_0/libs/parameter/doc/html/index.html не знаю, насколько это используется на практике, хотя   -  person Pablo    schedule 06.04.2012
comment
Ничего не выиграно? Я могу добавить любое количество параметров (в любом порядке) с минимальным изменением кода, я достигаю большой гибкости. Есть, конечно, некоторые правила, но реализация навязывает их пользователю настолько, насколько это возможно. Boost.parameter значительно увеличивает время компиляции, чего не должно делать мое решение. Он также использует несколько макросов, которые, на мой взгляд, не так уж и современны C++.   -  person Fredrik    schedule 06.04.2012
comment
@Fredrik: и опять же, как это вам заметно помогает? Конечно, это звучит круто, но действительно люди испытывают трудности с своевременным созданием надежного кода, потому что им приходится передавать аргументы в функцию в определенном порядке? Я так не думаю.   -  person Ed S.    schedule 06.04.2012
comment
@Fredrik: Boost.parameter значительно увеличивает время компиляции Используйте предварительно скомпилированные заголовки, и вы не увидите проблем. Почему никто не использует их сейчас, для меня полная загадка. Глупо обвинять библиотеку в том, что вы неэффективно используете свой набор инструментов.   -  person ildjarn    schedule 06.04.2012
comment
Ну, это не подходит для каждого решения/функции, дизайн класса должен помочь вам, только если у вас большое количество параметров. Каждый может легко передать некоторые типы в неправильном порядке. Но я понимаю ваши мысли, спасибо!   -  person Fredrik    schedule 06.04.2012
comment
Этот пример достаточно неудобочитаем, чтобы поместить среднего специалиста по сопровождению кода в бесконечный цикл... Смысл использования шаблонов в том, чтобы заставить компилятор автоматически угадывать аргументы шаблона, когда это возможно. В идеале код, который вызывает функции/методы шаблона, вообще не должен содержать скобок < или > (классы — это отдельная история, но они должны быть типизированы). Что-то вроде unsigned long & n = param<ref<unsigned long> >() непонятно. Однако что-то вроде unsigned long n; getParam(n);, где getParam является шаблоном <typename T> void getParam(T&), гораздо легче читать.   -  person SigTerm    schedule 06.04.2012
comment
@Fredrik: Boost.parameter значительно увеличивает время компиляции, время компиляции стоит меньше, чем время разработки, IMO. Сколько времени требуется для компиляции? Час? И сколько времени нужно, чтобы внедрить ваше решение или объяснить его кому-то?   -  person SigTerm    schedule 06.04.2012
comment
@ildjarn: Я знаю это, извините, но на самом деле я имею в виду, что, на мой взгляд, он слишком велик. Мне нужен один файл, вот так просто. Мой класс составляет 200-250 строк кода, насколько велик параметр boost.parameter, если включить все зависимости (за исключением STL). Я считаю, что это в значительной степени, но я не фанат повышения.   -  person Fredrik    schedule 06.04.2012
comment
@Fredrik: это не подходит для каждого решения / функции, IMo, когда программист слишком увлечен возможностями, предлагаемыми языком, он (или она) начинает тратить время на разработку чрезмерно сложных решений, которые ему на самом деле не нужны. Я предлагаю придерживаться принципа KISS.   -  person SigTerm    schedule 06.04.2012
comment
@Fredrik: Чтобы использовать Boost.Parameter, нужно только сделать #include <boost/parameter.hpp> - один файл. Кого волнует, что еще включает в себя это?   -  person ildjarn    schedule 06.04.2012
comment
@Fredrik: Мой класс состоит из 200-250 строк кода, что означает, что вы потратили на это полдня. Параметр boost уже написан, протестирован, отлажен, найдены и исправлены ошибки, и он поддерживается другими людьми. Большая проблема с вашим кодом в том, что мне нужно найти ваш basic_parameter_class и ref, чтобы понять, что именно здесь происходит. Поскольку вы по доброй воле забыли предоставить их реализацию, ваш код введёт мейнтейнера в ступор как минимум на час.   -  person SigTerm    schedule 06.04.2012
comment
Я знаю о KISS, именно поэтому я написал это. Я нахожу место в своем коде там, где оно подходит, но, конечно, пока это только одно место. Но я понимаю вашу точку зрения и все остальные, если это имеет значение :-) @ildjarn: Вы шутите, откройте этот файл, и вы найдете множество включений...   -  person Fredrik    schedule 06.04.2012
comment
@Fredrik: Следовательно, кого это волнует? Почему это имеет значение? (Подсказка: нет.)   -  person ildjarn    schedule 06.04.2012
comment
@ildjarn: Да, это действительно так, boost.parameter - это огромная зависимость для меня, и для меня это не нужно.   -  person Fredrik    schedule 06.04.2012
comment
@Fredrik: Как библиотека только для заголовков может быть огромной зависимостью? Это нонсенс.   -  person ildjarn    schedule 06.04.2012
comment
@Fredrik: Ваш класс противоположен KISS, потому что вы решили заново изобрести колесо и использовать неинтуитивный синтаксис вместо использования существующего решения, которое бесплатно поддерживается опытными разработчиками C++ (нулевая стоимость для вас). Подробности смотрите в моем ответе.   -  person SigTerm    schedule 06.04.2012
comment
@ildjarn: Если вы указываете, что только заголовки всегда маленькие, вы ошибаетесь, вы можете поместить массив с кодом внутри файлов заголовков, но это не значит, что вы должны это делать. Моя цель - иметь только зависимость от STL, за некоторыми исключениями. Но это не по теме. SigTerm : Я знаю, но на самом деле я пробовал это один раз, но получилось не очень хорошо, я не понял его, и у меня были серьезные ошибки компиляции, но это было давно. Я, наверное, лучший программист сегодня.   -  person Fredrik    schedule 06.04.2012
comment
@Fredrik Я никогда не говорил маленький, я сказал неуместный. Здесь нет ссылок, поэтому проблемы нет (или, по крайней мере, вы не сказали, в чем проблема).   -  person ildjarn    schedule 06.04.2012
comment
Переместите расширенные обсуждения в чат Stack Overflow.   -  person Tim Post♦    schedule 06.04.2012


Ответы (2)


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

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

test(( name("Special :-)"), ref<unsigned long>(num) ));

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

  1. На первый взгляд это выглядит как пример "самого неприятного синтаксического анализа", потому что вы используете двойной круглые скобки. Итак, я предполагаю, что test является переменной, и должен задаться вопросом, не забыли ли вы написать тип переменной. Затем мне приходит в голову, что эта штука действительно компилируется. После этого я должен задаться вопросом, является ли это экземпляром немедленно уничтоженного класса теста типа, и вы используете имена в нижнем регистре для всех типов классов.
  2. Затем я обнаруживаю, что на самом деле это вызов функции. Здорово.
  3. Фрагмент кода теперь выглядит как вызов функции с двумя аргументами.
  4. Теперь мне становится очевидно, что это не может быть вызов функции с двумя аргументами, потому что вы использовали двойные скобки.
  5. Итак, СЕЙЧАС я должен понять, что, черт возьми, происходит внутри ().
  6. Я помню, что есть оператор запятая (который я никогда не видел в реальном коде C++ в течение последних 5 лет), который отбрасывает предыдущий аргумент. ТАК СЕЙЧАС я должен задаться вопросом, что это за полезный побочный эффект name() и что такое name() - вызов функции или тип (потому что вы не используете прописные/строчные буквы, чтобы различать класс/функцию (т.е. Test — это класс, а test — функция), и у вас нет префиксов C).
  7. После поиска name в исходном коде я обнаружил, что это класс. И что он перегружает оператор ,, так что фактически больше не отбрасывает первый аргумент.

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

Теперь давайте рассмотрим пример QString.

 QString status = QString("Processing file %1 of %2: %3").arg(i).arg(total).arg(fileName);

Допустим, я вижу это впервые в жизни. Вот как проходит мой мыслительный процесс:

  1. Есть переменная status типа QString.
  2. Он инициализируется временной переменной типа QString().
  3. ... после вызова метода QString::arg. (Я знаю, что это метод).
  4. Я ищу .arg в документации, чтобы узнать, что он делает. , и обнаружите, что он заменяет записи в стиле %1 и возвращает QString&. Таким образом, цепочка из .arg() вызовов сразу обретает смысл. Обратите внимание, что что-то вроде QString::arg может быть создано по шаблону, и вы сможете вызывать его для различных типов аргументов, не указывая вручную тип аргумента в <>.
  5. Этот фрагмент кода теперь имеет смысл, поэтому я перехожу к другому фрагменту.

выглядит более "современно" C++

«Новый и блестящий» иногда означает «глючный и сломанный» (slackware linux был построен на похожей идее). Неважно, выглядит ли ваш код современно. Он должен быть удобочитаемым, он должен делать то, для чего предназначен, и вы должны тратить как можно меньше времени на его написание. т.е. вы должны (личная рекомендация) стремиться «реализовать максимальный объем функций за минимальное время с минимальными затратами (включая обслуживание)», но получить за это максимальное вознаграждение. Также имеет смысл следовать принципу KISS.

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

person SigTerm    schedule 06.04.2012
comment
Это очень хороший ответ, который ясно демонстрирует суровый кошмар обслуживания, который уловка спрашивающего навсегда сделает для каждого человека, которому когда-либо приходилось смотреть на что-то настолько запутанное, что вводит в заблуждение. Молодец: +1. - person tchrist; 22.07.2015
comment
Большая часть вашего аргумента сводится к проблеме курицы и яйца - люди не используют ее, потому что не сразу понятно, что она означает, но не сразу понятно, что она означает, потому что она не используется часто. Я предполагаю, что есть лучшие причины избегать этого, чем то, что люди не понимают его сразу, если они не видели чего-то подобного раньше (если бы это само по себе было достаточной причиной, я полагаю, что новые языки/языковые конструкции были бы крайне редки). - person Bernhard Barker; 22.07.2015
comment
returns QString& вы можете сделать это на xvalues ​​?? - person v.oddou; 13.04.2018

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

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

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

person bitmask    schedule 06.04.2012
comment
Спасибо за ответ, но определить намного проще, для меня это настолько просто, насколько это возможно, почти как простая функция. Скорость также не намного медленнее, потому что все сохраняется в непрерывной памяти, часто 100% в стеке. - person Fredrik; 06.04.2012
comment
@Fredrik: Ваше использование оператора последовательности , необычно, и все остальное, чем просто. Перегрузка операторов чрезвычайно заманчива для неправильного использования, и это неправильное использование. В этом отношении это противоположно простому использованию. Также я предполагаю, что у вас есть какая-то магия в ваших типах параметров, которая не является тривиальной, поэтому ее непросто реализовать. Что я имел в виду под медленнее, так это; у вас есть все эти инструкции ветвления, которые так ненавидят процессоры, это плохо. С шаблонами у вас нет, вместо этого у вас есть много возможностей оптимизации, которых не хватает этому подходу, если я правильно понял. - person bitmask; 06.04.2012
comment
Также обратите внимание, что медленность — понятие относительное. Если это помогает вашей удобочитаемости/ремонтопригодности, не беспокойтесь об эффективности (ну, возможно, некоторые крошечные задницы, возможно, мыши, в зависимости от вашего проекта). Было сказано, что; Если операция немного медленнее, это может сложиться невероятно, если эта операция выполняется очень-очень часто (например, если вы реализуете DES, вы даже не заметите медленный анализ аргументов CLI, но ваш планировщик должен быть чертовски быстрым). - person bitmask; 06.04.2012