All streams
Search
Write a publication
Pull to refresh
16
0
Send message
Проблема в том, что значение по умолчанию Шредингера находится в суперпозиции — оно одновременно есть, когда мы пытаемся вызвать функцию, опуская его; и его нет, когда мы пытаемся наблюдать его.
Чтобы ваш код заработал, надо чтобы для func и other_func вызывались разные версии шаблонной функций. Этого можно достичь, например, завернув каждую функцию в свой функциональный объект:
call([](auto&&... args){ func(std::forward<decltype(args)>(args)...); });
call([](auto&&... args){ other_func(std::forward<decltype(args)>(args)...); });


А можно просто отказаться от плохой практики использования значения по умолчанию в пользу перегрузок и вызова функций согласно их сигнатурам:
void func(int x);
void func() {
    func(0);
}

...

call(static_cast<void(&)()>(func)); // static_cast необходим для выбора перегруженного варианта
call(static_cast<void(&)(int)>(func),10);
Именно. Но я этого не предлагаю, напротив, говорю, что такой вариант невозможен, а без него именованные параметры превратятся в дополнительную препроцессорную магию, когда имена вроде как есть и используются, но вроде как и нет, так как средствами языка невозможно узнать о их существовании или отсутствии (например при помощи sfinae). Какая-то квантовая механика, а не программирование.
Допустим:
void func(int x=0);

А теперь попробуем:
template<class F, class ... Args>
auto call(F f, Args&& ... args) {
    return f(std::forward<Args>(args)...);
}

...

call(func);

Или даже:
auto f = func;
f();


Значения по умолчанию практически ничем не отличаются от макросов, и всего-лишь делают замену на уровне препроцессора с:
func();

на:
func(0);


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

Неужели c++ недостаточно твердо стоит на двух костылях макросов и значений по умолчанию, что его еще нужно подпереть третьим?
Конечно в N4172 предложен вариант без утери обратной совместимости, но использование такого варианта может принести вместо удобства головную боль: например автор используемой третьесторонней библиотеки решит переименовать параметры, или вообще убрать их имена — имеет полное право. А про переносимость таких программ можно будет вообще забыть.
Именованные параметры не могут быть добавлены на уровень языка, без включения имен параметров в сигнатуру функции (сейчас в сигнатуре только типы и порядок параметров), что сильно ломает обратную совместимость, которая является одним из главных критериев для дальнейшего развития стандарта.
Всё с точностью до наоборот, константа как раз и выкидывается из объектного файла оптимизатором за ненадобностью. Нетронутая строка останется при -O1 и ниже.
А достаточно было включить -O2 или выше.
А теперь выполните
objdump -s -j .rodata sample
и удивитесь…
В исходниках все соответствующие конструкторы и операторы спрятаны, просто не стал писать в статью чтобы кода было не так много.

Куда спрятаны? У вас там default.

В смысле неинициализированные?

Извиняюсь, невнимательно прочитал код, в первом варианте у вас void Add(...), а во втором уже char.
И под каждый объект создается целый набор функторов, хранящих указатель на this, и не являющихся не только copy-safe, но и move-safe, и это без удаленных соответствующих конструкторов и операторов. Я уже молчу про неинициализированные фиктивные члены класса…
«Простой способ отстрелить всю ногу в C++. А почему бы и нет?»
Можно сделать, например, compile-time JSON-парсер, но т.к. сам JSON должен быть объявлен до компиляции, а не в runtime, практического смысла от этого никакого, кроме образовательного. С другой стороны можно сделать compile-time генератор парсеров для объявленной json-schema: парсер в runtime получает текст и выдает статически-типизированную структуру, если источник соответствует схеме, или исключение в противном случае.
Впрочем, это не совсем тот случай, на который я хотел бы обратить внимание. Подобная техника может быть крайне полезна не для парсеров, а для ORM, на что и нацелен пример из статьи. Все кто имел дело с статически-типизированными ORM в C++ прекрасно понимают как неудобно определять структуры данных без использования дополнительных препроцессоров.

PS. Кстати, следующую статью я как раз хотел посвятить части библиотеки моего С++ web-фреймворка (который хотелось бы полностью перевести на opensource) — библиотеке для работы с JSON. Однако, возможно более интересной темой была бы реализация ORM, использующего вышеозначенную технику (единственное, что меня смущает, — пока еще неопределенный статус данного типа литералов в будущем стандарте). Что вы думаете?

Information

Rating
Does not participate
Location
Москва и Московская обл., Россия
Date of birth
Registered
Activity

Specialization

Software Architect
PostgreSQL
C#
C++
Linux
Docker
Kubernetes
High-loaded systems
Designing application architecture
Database design