реализация функции-члена в зависимости от параметра шаблона

У меня есть следующая проблема:

template< typename T, size_t N, size_t... N_i >
class A
{  
  public:

    // ...

    // first implementation
    template< size_t M = sizeof...(N_i)+1, typename std::enable_if< M!=1, size_t >::type = 0 >
    A<T, N_i...> operator[]( size_t i )
    {
      A< T, N_i... > res{ ... };

      return res;
    }

    // second implementation
    template< size_t M = sizeof...(N_i)+1, typename std::enable_if< M==1, size_t >::type = 0 >
    T operator[]( size_t i )
    {
      return ... ;
    }
};

Как вы можете видеть выше, я пытаюсь реализовать класс A, который ожидает в качестве аргументов шаблона тип T (например, int или float) и sizeof...(N_i)+1-много size_t.

В зависимости от количества переданных size_t (т.е. sizeof...(N_i)+1) я буду использовать другую реализацию для функции-члена operator[](size_t) с другим типом результата:

  • одна реализация для случая sizeof...(N_i)+1 > 1 с типом возвращаемого значения A < T, N_i... > (называемая в коде "первой реализацией")
  • и один для случая sizeof...(N_i)+1 == 1 с типом возвращаемого значения T (называемый в коде "второй реализацией").

К сожалению, я понятия не имею, как это можно реализовать - решение выше не работает. У кого-нибудь есть идея?

Спасибо заранее.


person abraham_hilbert    schedule 13.10.2016    source источник


Ответы (2)


A<T, N_i...> недействителен для пустого N_i. В качестве обходного пути вы можете использовать косвенность:

template <typename, std::size_t ...>
struct PopFrontA
{
    using type = void; // Dummy type for A<T, N>
};

template< typename T, std::size_t N, std::size_t... N_i > class A;

template <typename T, std::size_t N, std::size_t N2, std::size_t ... Ns>
struct PopFrontA<T, N, N2, Ns...>
{
    using type = A<T, N2, Ns...>;
};

template <typename T, std::size_t ... Ns>
using PopFrontA_t = typename PopFrontA<T, Ns...>::type;

А потом

// first implementation
template< size_t M = sizeof...(N_i)+1, typename std::enable_if< M!=1, size_t >::type = 0 >
PopFrontA_t<T, N, N_i...>
operator[]( size_t i )
{
  A< T, N_i... > res{ /*...*/ };

  return res;
}

Демо

person Jarod42    schedule 13.10.2016
comment
Благодарю вас! Вы знаете, почему A<T, N_i...> не вызывает ошибку SFINAE для пустого N_i? - person abraham_hilbert; 23.10.2016
comment
@abraham_hilbert: A<T, N_i...> не зависит от параметра шаблона метода. A<T, N_i...> недействителен для любого значения параметра шаблона метода. - person Jarod42; 24.10.2016

Если вы измените

A< T, N_i... > res{ ... };

in

A< T, N_i... > res{ };

а также

return ... ;

in

return T{} ;

недостаточно?

--- ИЗМЕНИТЬ ---

Нет: как указал Jarod42 (спасибо!), недостаточно.

Поэтому я предлагаю следующее решение, основанное на специализации шаблона класса и std::conditional, чтобы избежать использования SFINAE.

#include <iostream>
#include <type_traits>

template< typename, size_t...>
class A;

template< typename T, size_t N, size_t... N_i >
class A<T, N, N_i...>
 {  
   public:
      template <typename Next = typename std::conditional<sizeof...(N_i),
                                           A<T, N_i...>, T>::type>
      Next operator[] (size_t i)
       { return Next{}; }
 };

int main(int argc, char* argv[]) 
 {
   A<int, 2, 4> a;

   std::cout << a[1][2] << std::endl;

   return 0;
 }

Если вы не хотите специализировать A, вы можете добавить подструктуру A, чтобы сделать грязную работу.

#include <iostream>
#include <type_traits>

template< typename T, size_t N, size_t... N_i >
class A
 {  
   template <typename U, size_t ... O_i>
      struct Next
       { using type = A<U, O_i...>; };

   template <typename U>
      struct Next<U>
       { using type = U; };

   public:
      using next_t = typename Next<T, N_i...>::type;

      next_t operator[] (size_t i)
       { return next_t{}; }
 };

int main(int argc, char* argv[]) 
 {
   A<int, 2, 4> a;

   std::cout << a[1][2] << std::endl;

   return 0;
 }
person max66    schedule 13.10.2016
comment
У вас будет ошибка после создания экземпляра A<T, N>. Демо - person Jarod42; 13.10.2016