Как группировать разные статические классы?

#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 -> для возвращаемого типа». Еще раз спасибо!