Здравствуйте, uzhas, Вы писали:
U>соглашусь. у функций может быть много объявлений и одно определение. необходимость протаскивать одинаковые имена для параметров во всех объявлениях сильно напряжет и сломает обратную совместимость. по сути имена параметров станет частью сигнатуры. со структурами такой проблему нет: при определении структуры надо прописать один раз имена свойствам
Почему это у функции много объявлений? Функция объявляется в хидере, дальше хидер подключается куда надо. Если у тебя это не так — то почему тебя не напрягает требование ODR для структур?
И это второй коммент про обратную совместимость, абикс писал уже. Каким боком это нововведение ее сломает?
Re: А почему нет вызова функций с именованными аргументами?
Здравствуйте, enji, Вы писали:
E>Тут недавно холиварчик был С vs С++, там как одно из преимуществ С была запись E>
E>struct T { int a, b; };
E>T t = {.a = 1, .b=42};
E>
E>Если с таким разбирается компилятор С, то что мешает компилятору С++ разобраться с E>
E>void f(int a, int b);
E>f(.a = 1, .b=42);
E>
E>Ваше мнение?
Смешались кони, люди. Почему вы спрашиваете про именованные аргументы и в с++ приводите пример функции?
В си у вас инициализируется структура, а в с++ у вас параметризованная функция.
Например в с мы имеем здоровую структуру, а мы там частенько иемеем здоровую структуру, так что же нам делать если не все поля структуры нам нужно указать без этой фичи?
Правильно, ничего. Ничего кроме нудного указывания всех полей, подряд.
Не знаю насчет аргументов метода, но по мне в некоторых случаях читаемость улучшается. А это нынче важно, очень.
Так что мое мнение, любая фича языка, которая улучшает читабельность кода, должна присутствовать. Конечно можно сувать это куда попало, но все же полезно было бы иметь.
Re[4]: А почему нет вызова функций с именованными аргументами?
Здравствуйте, enji, Вы писали:
E>Если с таким разбирается компилятор С, то что мешает компилятору С++ разобраться с E>
E>void f(int a, int b);
E>f(.a = 1, .b=42);
E>
E>Ваше мнение?
Моё мнение такое: экономит надо не время записи, а время чтения.
Запись: E>f(.a = 1, .b=42);
ничем не лучше, а даже хуже, чем:
const int a = 1;
const int b = 42;
f(a, b);
Но, что такое a? Что такое b? —
Вызов функции с константами определёнными по месту вызова — это экзотика для хорошего стиля.
Если в программе встречается более чем один вызов функции с одинаковыми не именованными константами, то это уже плохо. Следует либо поименовать константы, либо завернуть вызов функции в другую функцию, например:
inline void LastAnswerOnUltimateQuestion()
{
const int nAnswerNumber = 1;
const int nAnswerValue = 1;
f(nAnswerNumber, nAnswerValue);
}
Заметили ошибку? Легко исправить?
А вы бы её заметили, во всех разбросанных по коду вызовах:
// file: start.cpp
f(.nAnswerNumber = 1, .nAnswerValue = 42);
// file: background.cpp
f(.nAnswerNumber = 1, .nAnswerValue = 42);
// file: crash.cpp
f(.nAnswerNumber = 1, .nAnswerValue = 1);
?
Легко исправить?
Здравствуйте, Tilir, Вы писали:
T>Отвратительно. В сигнатуру должны входить только типы, имена должны быть up-to-me. Захотел и переименовал, не разрушив существующего кода.
Кстати, а почему? Функцию ведь вы не переименуете, не разрушив существующего кода. Имя функции "более стабильно", чем имя параметра? Ой, не уверен...
Re[5]: А почему нет вызова функций с именованными аргументами?
Здравствуйте, enji, Вы писали:
E>Здравствуйте, Abyx, Вы писали:
A>>ну возможно я погорячился насчет "обратной совместимости", A>>но тем не менее твоя идея требует чтобы имена параметров были одинаковые в пределах единицы трансляции. A>>а в тоннах легаси кода это не так.
E>Ну прям вот так уж и в тоннах? Разные имена параметров означают, что одна и таже функция определена по разному в двух разных хидерах. Насколько это частое явление?
Очень частое:
// foo.hvoid foo(int a, int b);
//foo.cppvoid foo( int/* a */, int/* b */ )
{
// а и b не используются, поэтому, в определении их закоментировали, чтобы избежать предупреждения компилятора.
}
Re[3]: А почему нет вызова функций с именованными аргументам
Здравствуйте, enji, Вы писали:
E>Дык и лямбды не нужны, можно же найти способ
Разумеется, лямбды не нужны.
Но в комитете по стандартизации тоже люди, лямбды позволяют писать однострочники-функторы для всяких STL-алгоритмов, писать на каждый чих классы-функторы действительно всех задолбало, и в конце концов лямбды это модно, поэтому они в стандарте. А именованные аргументы функций это скучно, никому ни для чего внятного не нужно, вызывает ассоциации с VBA и хоронит надежды на плюсовый ABI. Они и раньше-то были невелики "экспортировать класс из shared object" почти в точности означает "убить себя об стену". Но хоть для функций всё работало. Если не перегружать. И не шаблонные. И не... а черт с ними, пишем extern C.
И кроме того, Бьярни каждый раз когда ему говорят про именованные аргументы, припоминает что двадцать лет назад он эту булочку уже жевал и она ему не понравилась.
Поэтому ненужные лямбды в стандарте есть, а ненужных именованных аргументов -- нет и не будет.
Re[2]: А почему нет вызова функций с именованными аргументами?
T>Мне кажется, это harmful, хотя бы потому, что заставляет меня иметь имена параметров как часть сигнатуры функции. Отвратительно. В сигнатуру должны входить только типы, имена должны быть up-to-me. Захотел и переименовал, не разрушив существующего кода.
void funct(explicit int int_param, explicit double float_param);
funct(.int_param = 5, .float_param = 0.7); // ok
funct(5, 0.5); // compile error
И пусть будет часть сигнатуры, заодно можно и расширить метаданные по функциям.
Здравствуйте, PM, Вы писали:
PM>Не знаю, чем руководствовались разработчики (может быть это были 2 разных команды), но я не вижу преимуществ в способе задания параметров для CreateWindow перед RegisterClass.
Имхо в случае использования структуры проще добавлять/убирать параметры в будущем.
Re[2]: А почему нет вызова функций с именованными аргументами?
Теоретически, можно было бы паковать аргументы в структуру
foo(FooArgs{ .a = 1, .b = 42 })
А ещё, это напоминает смолток и объектив-си, где фрагменты имени функции чередуются с аргументами
[set_a: 1 b: 42] /* void set_a:b: (int, int) - или что-то типа того */
Интересно, что перегрузка функций с одним именем и разной арностью превращается чисто в лексическую
x and y -- Bool.and(y)
x and: y and: z -- Bool.and:and:(y,z)
x and: y and: z and: t -- Bool.and:and:and:(y,z,t)
x and: y and: z and: t and: u -- Bool.and:and:and:(y,z,t,u)
По правде сказать, C++ная перегрузка по арности почти везде чисто лексическая (компилятор сразу считает арность и дальше смотрит только на подходяще подмножество сигнатур).
Но есть одно место, где она чуть глубже проваливается. Это когда в функцию высшего порядка передаётся имя функции, и к этому имени подбирается сигнатура согласно типу. То есть, нельзя написать foo(and) в расчёте на то, что если foo принимает трёхместную функцию, то and разрешится как and:and:and: — это совсем разные имена.
В остальном же, смолтоковский подход — это именованные аргументы для бедных. Просто в реализации, строго в компиляции, а значит, ошибкоустойчиво.
(За вычетом того, что и смолток, и объектив-си — динамические, и ошибка в имени функции может выплыть иногда только в момент вызова).
Перекуём баги на фичи!
Re[5]: А почему нет вызова функций с именованными аргументами?
Здравствуйте, enji, Вы писали:
E>Здравствуйте, Abyx, Вы писали:
A>>ну возможно я погорячился насчет "обратной совместимости", A>>но тем не менее твоя идея требует чтобы имена параметров были одинаковые в пределах единицы трансляции. A>>а в тоннах легаси кода это не так.
E>Ну прям вот так уж и в тоннах? Разные имена параметров означают, что одна и таже функция определена по разному в двух разных хидерах. Насколько это частое явление?
ситуаций типа
int f(int);
int f(int x) { return x + 1; }
и наоборот
inf f(int x);
int f(int) { return 42; }
— полно, причем иногда в одной единице трансляции будет несколько forward-declaration одной функции
E>Собственно сейчас же как то живут с таким E>
E>/// a.cpp
E>struct T {
E> std::string s;
E>} a;
E>/// b.cpp
E>struct T {
E> int q;
E> std::string s;
E>} b;
E>
это нарушение ODR, с этим долго не живут.
In Zen We Trust
Re[5]: А почему нет вызова функций с именованными аргументами?
Здравствуйте, lgb, Вы писали:
PM>>Не знаю, чем руководствовались разработчики (может быть это были 2 разных команды), но я не вижу преимуществ в способе задания параметров для CreateWindow перед RegisterClass.
lgb>Имхо в случае использования структуры проще добавлять/убирать параметры в будущем.
В общем да, но для RegisterClass нет (см. RegisterClassEx), т.к. в структуре WNDCLASS отсутствует какая-либо информация о версии (поле с именем типа version, cbSize) и для RegisterClassEx пришлось создавать (сюрприз!) WNDCLASSEX
Наверно где-нибудь в "The Old New Thing" Raymond Chen когда-то уже рассказывал историю, почему так исторически сложилось, но на данный момент Win32 API не является образцом отличного дизайна.
Re[6]: А почему нет вызова функций с именованными аргументами?
Здравствуйте, Chorkov, Вы писали:
C>Очень частое: C>
C>// foo.h
C>void foo(int a, int b);
C>//foo.cpp
C>void foo( int/* a */, int/* b */ )
C>{
C> // а и b не используются, поэтому, в определении их закоментировали, чтобы избежать предупреждения компилятора.
C>}
C>
дык клиенту то виден foo.h, а не foo.cpp. А чем закончится попытка вызвать foo(.a=1, .b=2) внутри foo.cpp — вопрос. Может ошибкой компиляции, а может компилятор корректно обработает такую ситуацию — вроде как нет ничего сложного.
void foo(int a, int b);
void foo(int c, int d) {
}
foo(.a=1, .b=2); // вот тут уже однозначно ошибка компиляции
Re[5]: А почему нет вызова функций с именованными аргументами?
U>//a.cpp
U>void f(int, int); // определение где-то в другом месте: в другом файле или либе
U>void g()
U>{
U> f(4, 5);
U>}
U>
ну и какие тут проблемы? Просто нельзя вызвать f с именоваными параметрами. Если у тебя в одной единице f(int a, int b), а в другой — f(int b, int a) — ну попытка вызвать f(.a=1, .b=2) — нарушение ODR. Такое же, как иметь две структуры с одинаковыми именами и разным содержимым.
U>еще если вспомнить про экпортированные функции, то вообще труба, еще и ABI ломать надо
Что не так с экспортированными функциями и зачем ломать abi? Данная штука ведет себя примерно так же, как и параметры по умолчанию — т.е. разворачивается компилятором в обычный вызов функции сразу в месте вызова с той информацией, которая там компилятору доступна.
Re[2]: А почему нет вызова функций с именованными аргументами?
N>Смешались кони, люди. Почему вы спрашиваете про именованные аргументы и в с++ приводите пример функции? N>В си у вас инициализируется структура, а в с++ у вас параметризованная функция.
потому что в с++ есть конструкторы и если разрешить именованные аргументы функций, то чуток дополненный сишный код
struct T {
int a, b;
T(int a, int b) : a(a), b(b) {}
};
T t = {.a = 1, .b=42};
будет компилироваться
Re[2]: А почему нет вызова функций с именованными аргументам
Здравствуйте, B0FEE664, Вы писали:
BFE>Моё мнение такое: экономит надо не время записи, а время чтения. BFE>Запись: E>>f(.a = 1, .b=42); BFE>ничем не лучше, а даже хуже, чем: BFE>
BFE>const int a = 1;
BFE>const int b = 42;
BFE>f(a, b);
это все мило, но кто так пишет? Это просто убивает все однострочники.
Опять же, ты можешь ошибиться и написать f(b, a). А если я напишу f(.b=42, .a=1), то это будет корректно обработано (ну по крайней мере, если применить правила из питона).
Далее, функция может иметь аргументы по умолчанию — void f(a = 1, b = 2, c = 3). И ее можно будет вызывать f(.c=4). Твой вариант с константами тут не поможет...
BFE>Но, что такое a? Что такое b? —
ну вестимо в реальной жизни аргументы не называют a и b ...
BFE>Вызов функции с константами определёнными по месту вызова — это экзотика для хорошего стиля. BFE>Если в программе встречается более чем один вызов функции с одинаковыми не именованными константами, то это уже плохо. Следует либо поименовать константы, либо завернуть вызов функции в другую функцию, например: BFE>
Это к вопросу о нужности лямбд. Зачем нужна лямбда из одной строки, когда можно на 5 строках в другом месте файла изобразить повторно используемый функтор? Вроде как и низачем. Однако ж функторы особо не пишут, и вместо find_if фигачат цикл...
Здравствуйте, Ops, Вы писали:
Ops>А Boost.Parameter вроде ж не только пруф?
про буст я писал в исходном сообщении
BOOST_PARAMETER_NAME(graph) // Note: no semicolon
BOOST_PARAMETER_NAME(visitor)
BOOST_PARAMETER_NAME(root_vertex)
BOOST_PARAMETER_NAME(index_map)
BOOST_PARAMETER_NAME(color_map)
BOOST_PARAMETER_FUNCTION(
(void), // 1. parenthesized return type
depth_first_search, // 2. name of the function template
tag, // 3. namespace of tag types
(required (graph, *) ) // 4. one required parameter, and
(optional // four optional parameters, with defaults
(visitor, *, boost::dfs_visitor<>())
(root_vertex, *, *vertices(graph).first)
(index_map, *, get(boost::vertex_index,graph))
(in_out(color_map), *,
default_color_map(num_vertices(graph), index_map) )
)
)
{
// ... body of function goes here...
// use graph, visitor, index_map, and color_map
}
это ж, имхо, какой-то ужас...
Re[3]: А почему нет вызова функций с именованными аргументам
Здравствуйте, enji, Вы писали:
E>Здравствуйте, B0FEE664, Вы писали:
BFE>>Моё мнение такое: экономит надо не время записи, а время чтения. BFE>>Запись: E>>>f(.a = 1, .b=42); BFE>>ничем не лучше, а даже хуже, чем: BFE>>
BFE>>const int a = 1;
BFE>>const int b = 42;
BFE>>f(a, b);
так как читать такое намного проще читать. Однострочники меня не волнуют.
E>Опять же, ты можешь ошибиться и написать f(b, a). А если я напишу f(.b=42, .a=1), то это будет корректно обработано (ну по крайней мере, если применить правила из питона). E>Далее, функция может иметь аргументы по умолчанию — void f(a = 1, b = 2, c = 3). И ее можно будет вызывать f(.c=4). Твой вариант с константами тут не поможет...
Это хорошие аргументы до тех пор, пока мы не думаем о виртуальных методах.
E>Это к вопросу о нужности лямбд. Зачем нужна лямбда из одной строки, когда можно на 5 строках в другом месте файла изобразить повторно используемый функтор? Вроде как и низачем. Однако ж функторы особо не пишут, и вместо find_if фигачат цикл...
Потому, что для чтения кода важна локальность определения. Редко, когда один и тот же функтор используется более одного раза в программе.
Ну и вообще, предложенный метод не достаточно радикален. Перепутать f(.a=1, .b=42) и f(.a=42, .b=1) тоже довольно просто.
Есть только один по настоящему хардкодный путь: не использовать никаких констант без user-defined суффиксов: