cpp-ru / ideas

Идеи по улучшению языка C++ для обсуждения
https://cpp-ru.github.io/proposals
Creative Commons Zero v1.0 Universal
91 stars 0 forks source link

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

Open evilenzo opened 1 year ago

evilenzo commented 1 year ago

Описание

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

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);

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

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

AndreyG commented 1 year ago

Варианты 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 это неявный шаблонный параметр внешней функции, или что-то другое.