#c #c 11 #static #variadic-templates #stdtuple
#c #c 11 #статический #переменные-шаблоны #стандартный набор
Вопрос:
У меня есть шаблонный статический класс с разными специализациями, например:
template<typename Parameter >
class MyClass
{};
template<>
class MyClass<Parameter1>
{
public:
static constexpr Integer myarray[]={0};
static constexpr Integer myarraysize=1;
};
template<>
class MyClass<Parameter2>
{
public:
static constexpr Integer myarray[]={0,1};
static constexpr Integer myarraysize=2;
};
Теперь я хотел бы как-то сгруппировать эту информацию в новый класс
template<typename MyClass1, typename MyClasses... >
class MyGroupClass{
//do something...}
где я мог бы указать в качестве переменных параметров шаблона разные классы, а затем я мог бы получить доступ к различным статическим методам.
Например, я хотел бы получить доступ к чему-то, MyGroupClass[n]::myarraysize
myarraysize
связанному с n- MyClass
м.
Я полагаю, что мог бы создать кортеж (так имеющий std::get<n>()
), но мне не очень понятно, как это сделать, поскольку у меня нет конструкторов для таких отдельных статических классов. В конце концов, классы статичны.
Возможно ли достичь того, чего я хочу? Если да, не могли бы вы, пожалуйста, просветить меня? Спасибо.
Ответ №1:
Я хотел бы использовать что-то вроде MyGroupClass[n]:: myarraysize для доступа к myarraysize, относящемуся к n-му MyClass. Я полагаю, я мог бы создать кортеж (поэтому, имея std::get()),
Мне кажется, что вы должны различать два случая.
(1) когда myarraysize
в разных классах разные типы, вы можете создать a std::tuple
разных размеров, которые можно использовать std::get()
для извлечения значений.
На примере
template <typename ... Ts>
struct MyGroupStruct
{
const std::tuple<decltype(Ts::myarraysize)...> tpl { Ts::myarraysize... };
template <std::size_t N>
auto get () const -> decltype(std::get<N>(tpl))
{ return std::get<N>(tpl); }
};
Начиная с C 14, вы можете избежать конечного возвращаемого типа для get()
и писать просто
template <std::size_t N>
auto get () const
{ return std::get<N>(tpl); }
Обратите внимание, что функция MyGroupStruct::get()
получает index ( N
) в качестве параметра шаблона. Поэтому ему нужно известное значение времени компиляции. Это необходимо, потому что тип, возвращаемый методом шаблона, отличается в зависимости от индекса, поэтому должно быть известно время компиляции.
(2) когда все myarraysize
в разных классах имеют один и тот же тип, вы также можете создать a std::array
этого типа; что-то вроде
template <typename ... Ts>
struct MyGroupStruct
{
using myType = typename std::tuple_element<0u,
std::tuple<decltype(Ts::myarraysize)...>>::type;
const std::array<myType, sizeof...(Ts)> arr {{ Ts::myarraysize... }};
myType amp; get (std::size_t n)
{ return arr[n]; }
};
Обратите внимание, что в этом случае возвращаемое значение for get()
всегда одно и то же, поэтому он может получать индекс во время выполнения в качестве параметра (не шаблона).
Ниже приведен полный пример компиляции для случая разных типов (на основе кортежей)
#include <tuple>
#include <string>
#include <iostream>
struct Par1 {};
struct Par2 {};
struct Par3 {};
struct Par4 {};
template <typename>
struct MyStruct;
template <>
struct MyStruct<Par1>
{ static constexpr int myarraysize {1}; };
constexpr int MyStruct<Par1>::myarraysize;
template <>
struct MyStruct<Par2>
{ static constexpr long myarraysize {2l}; };
constexpr long MyStruct<Par2>::myarraysize;
template <>
struct MyStruct<Par3>
{ static constexpr long long myarraysize {3ll}; };
constexpr long long MyStruct<Par3>::myarraysize;
template <>
struct MyStruct<Par4>
{ static const std::string myarraysize; };
const std::string MyStruct<Par4>::myarraysize {"four"};
template <typename ... Ts>
struct MyGroupStruct
{
const std::tuple<decltype(Ts::myarraysize)...> tpl { Ts::myarraysize... };
template <std::size_t N>
auto get () const -> decltype(std::get<N>(tpl))
{ return std::get<N>(tpl); }
};
int main ()
{
MyGroupStruct<MyStruct<Par1>, MyStruct<Par2>,
MyStruct<Par3>, MyStruct<Par4>> mgs;
std::cout << mgs.get<0>() << std::endl;
std::cout << mgs.get<1>() << std::endl;
std::cout << mgs.get<2>() << std::endl;
std::cout << mgs.get<3>() << std::endl;
static_assert( std::is_same<int const amp;,
decltype(mgs.get<0>())>::value, "!" );
static_assert( std::is_same<long const amp;,
decltype(mgs.get<1>())>::value, "!" );
static_assert( std::is_same<long long const amp;,
decltype(mgs.get<2>())>::value, "!" );
static_assert( std::is_same<std::string const amp;,
decltype(mgs.get<3>())>::value, "!" );
}
А теперь полный пример компиляции для случая равных типов (на основе массива)
#include <tuple>
#include <array>
#include <string>
#include <iostream>
struct Par1 {};
struct Par2 {};
struct Par3 {};
struct Par4 {};
template <typename>
struct MyStruct;
template <>
struct MyStruct<Par1>
{ static constexpr int myarraysize {1}; };
constexpr int MyStruct<Par1>::myarraysize;
template <>
struct MyStruct<Par2>
{ static constexpr int myarraysize {2}; };
constexpr int MyStruct<Par2>::myarraysize;
template <>
struct MyStruct<Par3>
{ static constexpr int myarraysize {3}; };
constexpr int MyStruct<Par3>::myarraysize;
template <>
struct MyStruct<Par4>
{ static const int myarraysize {4}; };
const int MyStruct<Par4>::myarraysize;
template <typename ... Ts>
struct MyGroupStruct
{
using myType = typename std::tuple_element<0u,
std::tuple<decltype(Ts::myarraysize)...>>::type;
const std::array<myType, sizeof...(Ts)> arr {{ Ts::myarraysize... }};
myType amp; get (std::size_t n)
{ return arr[n]; }
};
int main ()
{
MyGroupStruct<MyStruct<Par1>, MyStruct<Par2>,
MyStruct<Par3>, MyStruct<Par4>> mgs;
std::cout << mgs.get(0) << std::endl;
std::cout << mgs.get(1) << std::endl;
std::cout << mgs.get(2) << std::endl;
std::cout << mgs.get(3) << std::endl;
static_assert( std::is_same<int const amp;,
decltype(mgs.get(0))>::value, "!" );
}
— РЕДАКТИРОВАТЬ —
OP спрашивает
как изменить код, если я хочу получить доступ также к myarray, а не только к myarraysize?
С массивом в стиле C немного сложнее, потому что вы не можете инициализировать кортеж массивов в стиле C массивами в стиле C.
Я предлагаю вам использовать кортеж ссылок на массивы в стиле C.
Итак, учитывая некоторые MyClass
значения только с myarray
(зачем добавлять размер, когда вы можете его вывести?)
template <typename>
struct MyStruct;
template <>
struct MyStruct<Par1>
{ static constexpr int myarray[] {0}; };
constexpr int MyStruct<Par1>::myarray[];
// other MyStruct specializations ...
вы можете добавить кортеж ссылок (кортеж, не std::array
, потому int[1]
что , int[2]
, int[3]
и int[4]
все разные типы) и a std::array<std::size_t, sizeof...(Ts)>
для размеров.
Я имею в виду… вы можете написать что-то вроде следующего
template <typename ... Ts>
struct MyGroupStruct
{
std::tuple<decltype(Ts::myarray) amp; ...> const tpl { Ts::myarray... };
std::array<std::size_t, sizeof...(Ts)> const arr
{{ sizeof(Ts::myarray)/sizeof(Ts::myarray[0])... }};
template <std::size_t N>
auto getArr () const -> decltype(std::get<N>(tpl))
{ return std::get<N>(tpl); }
std::size_t getSize (std::size_t n) const
{ return arr[n]; }
};
что это означает «const -> decltype(std::get(tpl))»?
const
не имеет отношения к decltype()
.
const
после списка параметров метода скажите, что метод может использоваться также постоянным объектом, потому что не изменяет переменные-члены.
Что касается decltype()
поиска «завершающего возвращаемого типа» для получения дополнительной информации.
Короче говоря, для C 11 идея заключается в том, что в
auto foo () -> decltype(something)
{ return something; }
auto
скажите «look after -> для возвращаемого типа» и decltype(something)
является «типом something
«
Вы также можете написать
decltype(something) foo ()
{ return something; }
if something
известен перед списком аргументов функции, но auto
/ -> decltype(something)
form становится полезным, когда something
содержит параметры шаблона
На примере
template <typename T1, typename T2>
auto sum (T1 const amp; t1, T2 const amp; t2) -> decltype(t1 t2)
{ return t1 t2; }
Начиная с C 14, «конечный возвращаемый тип» используется реже, потому что вы можете просто написать
template <typename T1, typename T2>
auto sum (T1 const amp; t1, T2 const amp; t2)
{ return t1 t2; }
потому auto
что скажите компилятору «вывести возвращаемый тип из return
выражения» ( t1 t2
в данном случае из.
Это позволяет избежать большого количества избыточностей.
И можем ли мы использовать «auto» также в C 11?
auto
как возвращаемый тип? Без конечного возвращаемого типа?
К сожалению, доступно только начиная с C 14.
Далее следует еще один полный пример с myarray
и выведенными размерами.
#include <tuple>
#include <array>
#include <string>
#include <iostream>
struct Par1 {};
struct Par2 {};
struct Par3 {};
struct Par4 {};
template <typename>
struct MyStruct;
template <>
struct MyStruct<Par1>
{ static constexpr int myarray[] {0}; };
constexpr int MyStruct<Par1>::myarray[];
template <>
struct MyStruct<Par2>
{ static constexpr int myarray[] {0, 1}; };
constexpr int MyStruct<Par2>::myarray[];
template <>
struct MyStruct<Par3>
{ static constexpr int myarray[] {0, 1, 2}; };
constexpr int MyStruct<Par3>::myarray[];
template <>
struct MyStruct<Par4>
{ static constexpr int myarray[] {0, 1, 2, 3}; };
constexpr int MyStruct<Par4>::myarray[];
template <typename ... Ts>
struct MyGroupStruct
{
std::tuple<decltype(Ts::myarray) amp; ...> const tpl { Ts::myarray... };
std::array<std::size_t, sizeof...(Ts)> const arr
{{ sizeof(Ts::myarray)/sizeof(Ts::myarray[0])... }};
template <std::size_t N>
auto getArr () const -> decltype(std::get<N>(tpl))
{ return std::get<N>(tpl); }
std::size_t getSize (std::size_t n) const
{ return arr[n]; }
};
int main ()
{
MyGroupStruct<MyStruct<Par1>, MyStruct<Par2>,
MyStruct<Par3>, MyStruct<Par4>> mgs;
std::cout << mgs.getSize(0) << std::endl;
std::cout << mgs.getSize(1) << std::endl;
std::cout << mgs.getSize(2) << std::endl;
std::cout << mgs.getSize(3) << std::endl;
static_assert( std::is_same<std::size_t,
decltype(mgs.getSize(0))>::value, "!" );
std::cout << mgs.getArr<0>()[0] << std::endl;
std::cout << mgs.getArr<1>()[1] << std::endl;
std::cout << mgs.getArr<2>()[2] << std::endl;
std::cout << mgs.getArr<3>()[3] << std::endl;
static_assert( std::is_same<int const (amp;)[1],
decltype(mgs.getArr<0>())>::value, "!" );
static_assert( std::is_same<int const (amp;)[2],
decltype(mgs.getArr<1>())>::value, "!" );
static_assert( std::is_same<int const (amp;)[3],
decltype(mgs.getArr<2>())>::value, "!" );
static_assert( std::is_same<int const (amp;)[4],
decltype(mgs.getArr<3>())>::value, "!" );
}
Комментарии:
1. Спасибо за ответ. Но у меня все еще есть некоторые проблемы. 1) как изменить код, если я хочу получить доступ также к myarray, а не только к myarraysize? Действительно, доступ к размеру — это лишь одно из действий, которое я хотел бы выполнить. 2) Не были бы вы так любезны уточнить функцию get для случая кортежа? что это означает «const -> decltype(std::get<N>(tpl))»? И можем ли мы использовать «auto» также в C 11?
2. @Garo — Улучшен ответ. Надеюсь, это поможет.
3. вы очень четко изложили все, о чем я просил. Большое спасибо! Я не знал автоматической части «look after -> для возвращаемого типа». Еще раз спасибо!