Рассмотрим следующее:
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
псевдоним шаблона, но предоставьте функцию make_special
, похожую на C ++ 17:
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
требуется дополнительные фигурные скобки при использовании инициализации списка.
Есть ли другие способы эмулировать нечто подобное?