cpp-ru/ideas

Возможность использовать auto в качестве типового параметра другого типа

evilenzo opened this issue · 1 comments

Описание

На данный момент если мы хотим иметь какой-то констрейнт для шаблонного типа, мы пишем что-то в таком духе:

template <typename T>
void foo(std::optional<T> opt_arg);

Это полезно и для пользователя, т.к. в объявлении он явно видит, что аргумент, например, обёрнут optional'ом, и для разработчика, т.к. он получает автокомплит от IDE.
Но есть и несколько минусов, например, излишняя вербозность, в особенности, когда имя типа больше использоваться не будет.
С 20-м стандартом появилась возможность переписать код подобным образом:

template<typename T>
struct is_optional : std::false_type {};

template <typename T>
struct is_optional<std::optional<T>> : std::true_type {};

template <typename T>
concept Optional = is_optional<T>::value;

/* В некоторых случаях можно сделать так:
template <typename T>
concept Optional = std::is_same_v<std::optional<T::value_type>, T>;
*/

void foo(Optional auto opt_arg);

Но вербозность это не особо уменьшает и всё ещё имеет множество минусов, например, нужда реализовывать такие констрейнты под каждый тип.
Чтобы решить проблему, предлагается такая запись:

void foo(std::optional<auto> opt_arg);

Либо, как вариант, вообще самостоятельно не писать шаблонные параметры и разрешить такую запись:

void foo(std::optional opt_arg);

Мотивация

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

Сложности

Нужно решить вопрос с несколькими параметрами у типа. Пример:

template <typename A, typename B>
struct TwoParams {};

// Вариант 1
void foo(TwoParams<auto, auto> arg);

// Вариант 2
void foo(TwoParams<auto> arg);

// Вариант 3
void foo(TwoParams<auto...> arg);

В случае с третьим вариантом есть вопросы по другим нюансам, например, когда таких аргументов несколько.

Полезные ссылки:

  • https://quick-bench.com/ - онлайн бенчмарк, поможет вам продемонстрировать эффективность вашего подхода
  • https://godbolt.org/ - онлайн дизассемблер
  • https://eel.is/c++draft/ - черновик стандарта C++ с возможностью ссылаться на конкретные параграфы
  • https://wg21.link/ - универсальная ссылка на международные proposal или баги в С++, например https://wg21.link/P1000 ссылается на документ P1000, а https://wg21.link/cwg100 на 100 баг в ядре языка

Варианты 2 и 3 кажутся нелогичными/неправильными, а 1 даже уже работает в GCC но с некоторыми ограничениями (https://gcc.godbolt.org/z/6xGTPo49h):

template <typename A, typename B>
struct TwoParams {};

void foo(TwoParams<auto, auto> arg);

void test() {
    foo(TwoParams<int, int>());
    foo(TwoParams<short, long>());
}

void bar(void(*)(auto)); // not ok

template<typename T>
using FPtr = void(*)(T);

void baz(FPtr<auto>);   // ok

На примере bar видно, что есть некоторые проблемы в том, чтобы заходя "вглубь" типа понимать, auto это неявный шаблонный параметр внешней функции, или что-то другое.