Частичная специализация шаблона для приложения с частичным параметром шаблона не работает GCC 4.8.1

В основе моего класса, основанного на политике, лежит адаптер контейнера, который предоставляет интерфейс для преобразования между различными контейнерами. Он параметризуется типом T и параметром шаблона шаблона Container. Чтобы заставить его работать со стандартными контейнерами, мне нужно частично применить некоторые из их аргументов, например, распределитель или размер массива. Вот как я это делаю:

template< typename T >
struct vector_{
  using policy = std::vector<T>; //default allocator
}; 

или в случае, если это доставляет мне неприятности:

 //data_adapter expects template template parameter that takes one type-argument,
 //but sadly std::array is a template<typename, size_t>
 //so we need to partially apply the size_t parameter
namespace array_detail{
  template< size_t N >
  struct array_impl{
    template< typename T >
    using array_default = std::array<T, N>;
  };  //now we can write array_impl<32>::array_default which is template<typename T>
}

Проблема в том, что мне нужно частично специализировать data_adaptor для всех N из array_impl, и GCC 4.8.1, похоже, не учитывает мою специализацию при вызове своего конструктора. Вот код:

data_adapter.hpp:

//data_adapter.hpp
#ifndef __DATA_ADAPTER_HPP__
#define __DATA_ADAPTER_HPP__

#include <array>
#include <type_traits>

template <
  typename T,
  template< typename t >
  class Container
> class data_adapter
: protected Container< T >
{
protected:
  typedef Container<T> data_type;

public:
    //constructor forwarding
  using data_type::data_type;

    //const iterator access for cross-container conversion copying
  using data_type::cbegin;
  using data_type::cend;

  data_adapter() = default;

public:
  ~data_adapter() {}
};

  //SFINAE helper to test whether T is an iterator or not

template<typename T, typename = void>
struct is_iterator
{
   static constexpr bool value = false;
};

template<typename T>
struct is_iterator<T, typename std::enable_if<!std::is_same<typename std::iterator_traits<T>::value_type, void>::value>::type>
{
   static constexpr bool value = true;
};

  //data_adapter expects template template parameter that takes one type-argument,
  //but sadly std::array is a template<typename, size_t>
  //so we need to partially apply the size_t parameter
namespace array_detail{
  template< size_t N >
  struct array_impl{
    template< typename T >
    using array_default = std::array<T, N>;
  };  //now we can write array_impl<32>::array_default which is template<typename T>
}

//partial specialization for array_impl<N>::array_default for any N???

template< typename T, size_t N >
class data_adapter< T, array_detail::array_impl<N>::template array_default >
: protected array_detail::array_impl<N>::template array_default<T>
{
protected:
  typedef typename array_detail::array_impl<N>::template array_default<T> data_type;

public:
  using data_type::data_type;

  using data_type::cbegin;
  using data_type::cend;

    //why doesn't std::array implement this constructor?
    //is it because it has static size and conversion from a dynamic container is dangerous?

  template<
    typename InputIt,
    typename = typename
      std::enable_if<is_iterator<InputIt>::value>::type
  > data_adapter( InputIt begin, InputIt end )
  : data_type() {
    std::copy( begin, end, this->begin() );
  }

public:
  ~data_adapter() {}
};

//still doesn't work with explicit instantiation
//template class data_adapter< int, array_detail::array_impl<32>::template array_default >;

#endif // __DATA_ADAPTER_HPP__

main.cpp:

//main.cpp
#include <algorithm>
#include <ctime>
#include <cstdint>
#include <iostream>
#include <random>


#include "data_adapter.hpp"

int main()
{
  std::mt19937 generator(time(NULL));
  std::uniform_int_distribution<unsigned char> uniform_symbol( 0, 255 );
  auto random_symbol =
    [ &generator, &uniform_symbol ]( int ){
      return uniform_symbol(generator);
    };

  std::vector< int > symbols(32);
  std::transform( symbols.cbegin(), symbols.cend(), symbols.begin(), random_symbol );

  data_adapter< int, array_detail::array_impl<32>::template array_default > adapter( symbols.cbegin(), symbols.cend() );

  std::for_each( symbols.begin(), symbols.end(), []( int s ){ std::cout << s << " "; } );

  return 0;
}

ошибки:

g++.exe -Wall -fexceptions  -std=c++11 -g  -Wall    -c C:\Users\windows\Desktop\data_test\main.cpp -o obj\Debug\main.o
C:\Users\windows\Desktop\data_test\main.cpp: In function 'int main()':
C:\Users\windows\Desktop\data_test\main.cpp:23:119: error: no matching function for call to 'data_adapter<int, array_detail::array_impl<32u>::array_default>::data_adapter(std::vector<int>::const_iterator, std::vector<int>::const_iterator)'
   data_adapter< int, array_detail::array_impl<32>::template array_default > adapter( symbols.cbegin(), symbols.cend() );
                                                                                                                       ^
C:\Users\windows\Desktop\data_test\main.cpp:23:119: note: candidates are:
In file included from C:\Users\windows\Desktop\data_test\main.cpp:9:0:
C:\Users\windows\Desktop\data_test\data_adapter.hpp:25:3: note: data_adapter<T, Container>::data_adapter() [with T = int; Container = array_detail::array_impl<32u>::array_default]
   data_adapter() = default;
   ^
C:\Users\windows\Desktop\data_test\data_adapter.hpp:25:3: note:   candidate expects 0 arguments, 2 provided
C:\Users\windows\Desktop\data_test\data_adapter.hpp:11:9: note: constexpr data_adapter<int, array_detail::array_impl<32u>::array_default>::data_adapter(const data_adapter<int, array_detail::array_impl<32u>::array_default>&)
 > class data_adapter
         ^

Он явно не учитывает конструктор, который я определил в частичной специализации для array_impl, поэтому можно сделать вывод, что data_adaptor в main.cpp создал экземпляр неспециализированной версии класса. Это не проблема SFINAE, поскольку компилятор будет жаловаться на отсутствие :: type в std :: enable_if. Даже если я явно создаю экземпляр специализированной версии, она все равно не работает. Что я здесь делаю не так?


РЕДАКТИРОВАТЬ: Вот минимальная версия, которая, как мне кажется, эквивалентна:

#include <iostream>
#include <array>

template<
  typename T,
  template < typename >
  class Container
> struct Foo{
  void test() const {
    std::cout << "Unspecialized Foo" << std::endl;
  }
};

template< size_t N >
struct array_{
  template< typename T >
  using policy = std::array<T, N>;
};

template<
  typename T, size_t N
> struct Foo< T, array_<N>::template policy >{
  void test() const {
    std::cout << "Foo< T, array<" << N << ">::policy >" << std::endl;
  }
};

int main()
{
    Foo< int, array_<10>::template policy > foo;
    foo.test();
    return 0;
}

Результат в GCC 4.8.1:

Unspecialized Foo

Почему называется неспециализированная версия?


РЕДАКТИРОВАТЬ 2: Кажется, это работает, когда я пишу это так:

#include <iostream>
#include <array>

template<
  typename container
> struct Foo{
  void test() const {
    std::cout << "Unspecialized Foo" << std::endl;
  }
};

template<
  typename T, size_t N
> struct Foo< std::array<T, N> >{
  void test() const {
    std::cout << "Foo< std::array< T, " << N << ">" << std::endl;
  }
};

int main()
{
    Foo< std::array<int, 10> > foo;
    foo.test();
    return 0;
}

Мне кажется, это ошибка GCC.


person tsuki    schedule 25.08.2014    source источник
comment
@ R.MartinhoFernandes Это не сработает для std::array, у которого есть целочисленный аргумент шаблона (но это решит проблему для std::vector и т. Д., У всех которых есть дополнительные аргументы шаблона помимо типа значения).   -  person Seg Fault    schedule 25.08.2014
comment
почему std :: array не реализует этот конструктор? Потому что, если бы у него был предоставленный пользователем конструктор, он больше не был бы агрегатом и не мог бы быть инициализирован с помощью агрегатной инициализации.   -  person T.C.    schedule 25.08.2014
comment
Учитывая, что g ++ и clang согласны с вашим исходным кодом, я несколько сомневаюсь, что это это ошибка компилятора.   -  person T.C.    schedule 25.08.2014
comment
Я считаю, что поведение g ++ правильное. Сопоставление частичной специализации выполняется с использованием вывода аргументов шаблона, и все, что находится слева от ::, является невыведенным контекстом, что означает, что компилятор не может вывести N в вашей частичной специализации, и, следовательно, используется базовый шаблон.   -  person T.C.    schedule 25.08.2014


Ответы (1)


Это не ошибка компилятора. §14.5.5.1 [temp.class.spec.match] / p1-2 указывает, что

Когда шаблон класса используется в контексте, требующем создания экземпляра класса, необходимо определить, будет ли создание экземпляра создано с использованием основного шаблона или одной из частичных специализаций. Это делается путем сопоставления аргументов шаблона специализации шаблона класса со списками аргументов шаблона частичных специализаций.

  • Если найдена ровно одна подходящая специализация, экземпляр создается на основе этой специализации.
  • Если найдено более одной подходящей специализации, правила частичного порядка (14.5.5.2) используются для определения того, является ли одна из специализаций более специализированной, чем другие. Если ни одна из специализаций не является более специализированной, чем все другие соответствующие специализации, тогда использование шаблона класса неоднозначно и программа плохо сформирована.
  • Если совпадений не найдено, экземпляр создается из основного шаблона.

Частичная специализация соответствует заданному фактическому списку аргументов шаблона, если аргументы шаблона частичной специализации могут быть выведены из фактического списка аргументов шаблона (14.8.2).

При этом используются стандартные правила вывода аргументов шаблона, и все, что находится слева от :: (технический термин - спецификатор вложенного имени в квалифицированном идентификаторе), не является -выводимый контекст (§14.8.2.5 [temp.deduct.type] / p5), означающий, что компилятор не сможет вывести N; поскольку дедукция не выполняется, частичная специализация не является совпадением, и используется базовый шаблон.

person T.C.    schedule 25.08.2014
comment
Вы, сэр, волшебник. Спасибо. Я просто использовал указанное вами правило в реализации implicit_cast! - person tsuki; 25.08.2014