Почему не может быть передана структура как значение в качестве параметра шаблона не-типа?
Параметры шаблона непигового типа, очевидно, являются типами, которые не являются типами, например:
template<int x>
void foo() { cout << x; }
В этом случае есть другие варианты, кроме int
, и я хотел бы назвать этот отличный ответ.
Теперь есть одна вещь, которая меня пугает: structs. Рассмотрим:
struct Triple { int x, y, z; };
Triple t { 1, 2, 3 };
template<Triple const& t>
class Foo { };
Теперь, используя обычную нетопическую ссылочную семантику, мы можем написать:
Foo<t> f;
Что стоит отметить, что t
не может быть constexpr
или даже const
, потому что это подразумевает внутреннюю привязку, что в основном означает, что строка не будет компилироваться. Мы можем обойти это, объявив t
как const extern
. Это может быть немного странно, но тот, который действительно заставило меня задуматься, почему это невозможно:
Foo<Triple { 1, 2, 3 }> f;
Мы получаем очень приличную ошибку от компилятора:
error: Triple{1, 2, 3}
не является допустимым аргументом шаблона для типа const Triple&
, потому что это не значение lvalue.
Мы не можем указать Triple
в шаблоне по значению, потому что это запрещено. Однако я не понимаю реальной проблемы с этой небольшой строкой кода. Какова причина отсутствия возможности использования структур как значений. Если я могу использовать три int
s, почему бы не создать структуру из трех целых чисел? Если он имеет только тривиальные специальные элементы, он не должен быть действительно различным в обработке, чем просто три переменные.
Ответы
Ответ 1
Было бы легко заставить этот бит работать, но тогда люди будут жаловаться на то, что использование параметров структуры шаблона не работает во всех тех же ситуациях, что и другие параметры шаблона (рассмотрим частичную специализацию или что делать с operator==
).
На мой взгляд, это слишком грязно, чтобы получить целый пирог, и получить только один крошечный кусочек недостаточно, и, возможно, больше расстраивает. Выполнение этой крошечной работы не даст мне больше возможностей, чем что-то вроде следующего, которое имеет дополнительное преимущество работы со всеми видами вещей (включая частичные специализации) из коробки.
template <int X, int Y, int Z>
struct meta_triple {
// static value getters
static constexpr auto x = X;
static constexpr auto y = Y;
static constexpr auto z = Z;
// implicit conversion to Triple
constexpr operator Triple() const { return { X, Y, Z }; }
// function call operator so one can force the conversion to Triple with
// meta_triple<1,2,3>()()
constexpr Triple operator()() const { return *this; }
};
Ответ 2
Вы можете определить t
как const extern
, давая ему внешнюю привязку. Затем работает конструкция:
struct Triple { int x, y, z; };
const extern Triple t { 1, 2, 3 };
template<Triple const& t>
class Foo { };
Foo<t> f;
Живой пример.
Причина, по которой вы не можете передать временный параметр ссылочного шаблона, заключается в том, что параметр является ссылкой.. Вы получите ту же ошибку, если параметр шаблона был const int&
, и вы попытался пройти 7
. Пример.
ИЗМЕНИТЬ
Разница между тремя int
и структурой, содержащей три int
, состоит в том, что все литералы типа int
действительно являются одним и тем же значением (все вхождения 7
составляют всего семь), а каждый вызов конструктора структура концептуально создает новый экземпляр. Возьмите этот гипотетический пример:
template <Triple t>
struct Foo {};
Foo<Triple {1, 2, 3}> f1;
Foo<Triple {1, 2, 3}> f2;
Я думаю, что это приведет к дополнительной сложности для "сопоставления" этих двух вызовов-конструкторов с тем же экземпляром шаблона.