【问题标题】:Partial template specialization for partial template parameter application not working GCC 4.8.1部分模板参数应用程序的部分模板专业化不起作用 GCC 4.8.1
【发布时间】:2014-08-25 10:22:35
【问题描述】:

我的基于策略的类的根是一个容器适配器,它为不同容器之间的转换提供接口。它由类型 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>
}

问题是我需要对array_impl 中的所有 N 部分专门化 data_adaptor,而 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定义的构造函数,所以结论是main.cpp中的data_adaptor实例化了类的非特化版本。这不是 SFINAE 问题,因为编译器会抱怨 std::enable_if 中缺少 ::type。即使,如果我明确地实例化专用版本,它仍然不起作用。我在这里做错了什么?


编辑: 这是我认为等效的最小版本:

#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 错误。

【问题讨论】:

  • @R.MartinhoFernandes 这对于具有整数模板参数的std::array 不起作用(但它可以解决std::vector 等的问题,它们都有超出值类型的额外模板参数)。
  • "为什么 std::array 不实现这个构造函数?"因为如果它有一个用户提供的构造函数,那么它将不再是一个聚合并且不能使用聚合初始化来初始化。
  • 考虑到 g++ 和 clang agree on your original code,我有点怀疑这是一个编译器错误。
  • 我相信g++的行为是正确的。部分特化匹配是使用模板参数推导完成的,:: 左侧的任何内容都是非推导上下文,这意味着编译器无法在您的部分特化中推导 N,因此使用基本模板。

标签: c++ templates gcc c++11 partial-specialization


【解决方案1】:

这不是编译器错误。 §14.5.5.1 [temp.class.spec.match]/p1-2 指定

当类模板用于需要 类的实例化,有必要确定是否 实例化将使用主模板或其中之一生成 部分专业。这是通过匹配模板来完成的 带有模板的类模板特化的参数 部分特化的参数列表。

  • 如果恰好找到一个匹配的特化,则从该特化生成实例化。
  • 如果找到多个匹配的特化,偏序规则 (14.5.5.2) 用于确定其中一项是否 专业比其他专业更专业。如果没有 专业化比所有其他匹配更专业 专业化,那么类模板的使用是模棱两可的, 程序格式不正确。
  • 如果未找到匹配项,则从主模板生成实例化。

部分特化匹配给定的实际模板参数列表 如果可以推导出偏特化的模板参数 来自实际的模板参数列表 (14.8.2)。

这使用标准模板参数推导规则,以及 :: 左边的任何内容(技术术语是 qualified-id中的 nested-name-specifier >) 是非推断上下文(§14.8.2.5 [temp.deduct.type]/p5),这意味着编译器将无法推断N;由于推导失败,偏特化不匹配,使用基模板。

【讨论】:

  • 您先生,是个巫师。谢谢你。我刚刚使用了您在implicit_cast 实现中提到的规则!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-18
  • 2018-08-25
  • 2018-07-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多