Как эмулировать направляющие вычеты для псевдонимов шаблонов?

Учтите следующее:

template <typename T, std::size_t N>
struct my_array
{
    T values[N];
};

Мы можем предоставить вычеты для my_array, что-то вроде

template <typename ... Ts>
my_array (Ts ...) -> my_array<std::common_type_t<Ts...>, sizeof...(Ts)>;

Теперь предположим, что my_array<T, 2> имеет какое-то особенное значение (но только значение, интерфейс и реализация остаются прежними), поэтому мы хотели бы дать ему более подходящее имя:

template <typename T>
using special = my_array<T, 2>;

Оказывается, что инструкции по выводу просто не работают для псевдонимов шаблонов, то есть это дает ошибку компиляции:

float x, y;

my_array a { x, y }; // works
special b { x, y }; // doesn't

Мы еще можем сказать special<float> b и будь счастлив. Однако предположим, что T какое-то длинное и утомительное имя типа, например, например std::vector<std::pair<int, std::string>>::const_iterator, В этом случае будет очень удобно иметь здесь вывод аргументов шаблона. Итак, мой вопрос: если мы действительно хотим special быть типом, равным (в некотором смысле) my_array<T, 2>и мы действительно хотим, чтобы работали гиды по выводам (или что-то подобное), как можно преодолеть это ограничение?

Заранее прошу прощения за несколько смутно поставленный вопрос.


Я придумал пару решений, оба с серьезными недостатками.

1) сделать special отдельный несвязанный класс с тем же интерфейсом, т.е.

template <typename T>
struct special
{
    T values[2];
};

template <typename T>
special (T, T) -> special<T>;

Это дублирование выглядит неловко. Кроме того, вместо написания таких функций, как

void foo (my_array<T, N>);

Я вынужден либо дублировать их

void foo (my_array<T, N>);
void foo (special<T>);

или делать

template <typename Array>
void foo (Array);

и полагаться на то, что интерфейс этих классов одинаков. Мне вообще не нравится этот вид кода (принимая что-либо и полагаясь исключительно на утку). Это может быть улучшено некоторыми SFINAE/ понятиями, но это все еще чувствует себя неловко.

2) Сделать special функция, т.е.

template <typename T>
auto special (T x, T y)
{
    return my_array { x, y };
}

Здесь нет дублирования типов, но теперь я не могу объявить переменную типа special, поскольку это функция, а не тип.

3) Оставь special псевдоним шаблона, но обеспечивает пре-C++17-подобный make_special функция:

template <typename T>
auto make_special (T x, T y)
{
    return my_array { x, y };
    // or return special<T> { x, y };
}

Это работает, в некоторой степени. Тем не менее, это не руководство по выводу, а смешивание классов, которые используют руководства по выводу с этим make_XXX функции звучит запутанно.

4) В соответствии с предложением @NathanOliver, сделайте special наследовать от my_array:

template <typename T>
struct special : my_array<T, 2>
{};

Это позволяет нам предоставлять отдельные руководства по вычетам для specialи не дублирование кода. Тем не менее, может быть разумно написать такие функции, как

void foo (special<int>);

который, к сожалению, не примет my_array<2>, Это может быть исправлено путем предоставления оператора преобразования из my_array<2> в special, но это означает, что у нас есть круговые преобразования между ними, что (по моему опыту) является кошмаром. Также special нужны дополнительные фигурные скобки при использовании инициализации списка.


Есть ли другие способы подражать чему-то подобному?

1 ответ

Решение

Простой ответ - подождать до C++20. Вполне вероятно, что дедукция шаблона класса научится просматривать шаблоны псевдонимов к тому времени (а также агрегаты и унаследованные конструкторы, см. P1021).


Кроме того, (1), безусловно, плохой выбор. Но выбор между (2), (3) и (4) во многом зависит от ваших вариантов использования. Обратите внимание, что для (4) направляющие вычетов также не наследуются до P1021, поэтому вам нужно будет скопировать направляющие вычетов базового класса, если вы пойдете по пути наследования.

Но есть и пятый вариант, который вы пропустили. Скучный:

special<float> b { x, y }; // works fine, even in C++11

Иногда этого достаточно?

Другие вопросы по тегам