Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/130.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/templates/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 模板类的模板成员上的模板专门化_C++_Templates_C++11_Specialization - Fatal编程技术网

C++ 模板类的模板成员上的模板专门化

C++ 模板类的模板成员上的模板专门化,c++,templates,c++11,specialization,C++,Templates,C++11,Specialization,这可能只是一个语法问题 所以我有一个模板类: template <typename String, template<class> class Allocator> class basic_data_object { template<typename T> using array_container = std::vector<T, Allocator<T>>; }; 但是,当我将std::vector作为最后一个参数传递时

这可能只是一个语法问题

所以我有一个模板类:

template <typename String, template<class> class Allocator>
class basic_data_object
{
  template<typename T>
  using array_container = std::vector<T, Allocator<T>>;
};
但是,当我将std::vector作为最后一个参数传递时,这种专门化似乎并不匹配

如果创建临时硬编码的typedef:

typedef basic_data_object<std::string, std::allocator<std::string>> data_object;
typedef基本数据对象数据对象;
并将其用于专业化,一切正常:

template <typename String, template<class> class Allocator, typename T>
struct get_data_object_value
<String, Allocator,
data_object::template array_container<T>>
{
};
模板
结构获取数据对象值
{
};
我错过了什么?:)



或者,什么是最好的(最小的/最干净的)方法来实现这一点?

编辑:这个答案只适用于GCC 4.8.1中的一个错误


如果您在您的专业化中删除关键字
模板
,您的代码将按预期工作:

template <typename String, template<class> class Allocator, typename T>
struct get_data_object_value
{
    void foo() { std::cout << "general" << std::endl; }
};

template <typename String, template<class> class Allocator, typename T>
struct get_data_object_value
<String, Allocator,
typename basic_data_object<String, Allocator>::array_container<T>>
//                                         ^^^^^^ no template!
{
    void foo() { std::cout << "special" << std::endl; }
};
template <typename S, template<class> class A, typename T>
struct get_data_object_value<S,A,std::vector<T,A>>
{ };
模板
结构获取数据对象值
{

空的FUTE(){STD::CUT< P> C++标准中说,在[ Time.Copy.Sq.Matt ]第2段:

部分专门化匹配给定的实际模板 参数列表,如果部分 专门化可以从实际模板中推导出来 参数列表(14.8.2)

14.8.2是[临时参数扣除],即描述函数模板的模板参数扣除的条款

如果您修改代码以使用类似的函数模板并尝试调用它,您将看到无法推导参数:

template <typename String, typename T>
void deduction_test(String,
                    typename basic_data_object<String, std::allocator>::template array_container<T>)
{ }

int main()
{
  deduction_test(std::string{}, std::vector<int, std::allocator<int>>{});
}
因此,相应地,当类模板使用该类型时,可以匹配它的部分专门化


如果不更改
get\u data\u object\u value
的定义,我看不到一种方法可以让它工作,但是如果这是一个选项,您可以不必推断
array\u container
类型,而是使用特征来检测类型是否是您想要的类型,并专门化特征的结果:

#include <string>
#include <vector>
#include <iostream>

template <typename String, template<class> class Allocator>
class basic_data_object
{
public:
  template<typename T>
  using array_container = std::vector<T, Allocator<T>>;

  template<typename T>
    struct is_ac : std::false_type { };

  template<typename T>
    struct is_ac<array_container<T>> : std::true_type { };
};

template <typename String, template<class> class Allocator, typename T, bool = basic_data_object<String, Allocator>::template is_ac<T>::value>
struct get_data_object_value
{
};

template <typename String, template<class> class Allocator, typename T>
struct get_data_object_value<String, Allocator, T, true>
{
  void f() { }
};

int main()
{
  get_data_object_value<std::string,std::allocator,std::vector<short>> obj;
  obj.f();
}
#包括
#包括
#包括
模板
类基本\u数据\u对象
{
公众:
模板
使用数组_container=std::vector;
模板
结构是_ac:std::false_type{};
模板
结构是_ac:std::true_type{};
};
模板
结构获取数据对象值
{
};
模板
结构获取数据对象值
{
void f(){}
};
int main()
{
获取\u数据\u对象\u值obj;
obj.f();
}

如果您需要几个类模板部分专门化,这实际上是不可伸缩的,因为您需要添加几个带有默认参数的
bool
模板参数。

出于某种原因,问题似乎源于模板的双重级别。我让您检查下面的3个测试用例,它们很简单:

  • 删除
    First
    的模板参数:按预期工作
  • 首先将
    设置为模板,但内部类型为普通类型:按预期工作
  • 首先生成
    和内部类型模板:编译,但输出是意外的
  • 注意:模板参数
    Allocator
    对于重现问题是无用的,因此我将其忽略

    注意:GCC(我相信是ideone的版本,4.8.1)和Clang(Coliru版本,3.4)都编译了代码,但产生了相同的令人困惑的结果

    从上述3个例子中,我推断:

    • 这不是一个不可推断的上下文问题;否则(2)为什么会起作用
    • 这不是别名问题;否则(1)为什么会起作用
    因此,要么问题比当前的提示更棘手,要么gcc和Clang都有缺陷

    编辑:感谢Jonathan Wakely,他耐心地教育了我,使我最终能够理解与本案相关的标准措辞及其应用。我现在尝试用自己的话(再次)解释这一点。请参考Jonathan的答案以获得确切的标准引用(全部位于[temp.decrete.type]中)

    • 在推导模板参数(Pi)时,无论是函数还是类,都会对每个参数独立进行推导
    • 每个参数需要为每个参数提供零个或一个候选Ci;如果一个参数将提供多个候选Ci,则它将不提供任何候选Ci
    • 因此,每个参数都会生成一个字典Dn:Pi->Ci,它将要推导的模板参数子集(可能为空)映射到它们的候选参数
    • 字典Dn按参数合并在一起:
      • 如果只有一个字典具有给定参数的候选项,则接受此参数,并使用此候选项
      • 如果多个字典对给定参数具有相同的候选项,则接受此参数,并使用此候选项
      • 如果多个字典对给定参数具有不同的不兼容(*)候选项,则此参数将被拒绝
    • 如果最终的字典是完整的(将每个参数映射到一个可接受的候选项),则演绎成功,否则将失败
    (*)似乎有可能从可用的候选者中找到一个“普通类型”……但这在这里并不重要

    现在我们可以将此应用于前面的示例:

    1) 存在单个模板参数
    T

    • 模式匹配
      std::vector
      typename First::template ArrayType
      (即
      std::vector
      ),我们得到D0:
      {T->int}
    • 合并唯一的字典会产生
      {T->int}
      ,因此
      T
      被推断为
      int
    2) 存在单个模板参数
    字符串

    • 模式匹配
      std::vector
      String
      ,我们得到D0:
      {String->std::vector}
    • 模式匹配
      std::vector
      typename First::ArrayType
      我们找到了一个不可推断的上下文(许多
      String
      的值可以匹配),我们得到D1:
      {}
    • 合并这两个字典将产生
      {String
      
      template <typename String, typename T>
      void deduction_test(String,
                          typename basic_data_object<String, std::allocator>::template array_container<T>)
      { }
      
      int main()
      {
        deduction_test(std::string{}, std::vector<int, std::allocator<int>>{});
      }
      
      template <typename String, typename T>
      void deduction_test(String,
                          typename data_object::template array_container<T>)
      { }
      
      int main()
      {
        deduction_test(std::string{}, std::vector<int, std::allocator<int>>{}); // OK
      }
      
      #include <string>
      #include <vector>
      #include <iostream>
      
      template <typename String, template<class> class Allocator>
      class basic_data_object
      {
      public:
        template<typename T>
        using array_container = std::vector<T, Allocator<T>>;
      
        template<typename T>
          struct is_ac : std::false_type { };
      
        template<typename T>
          struct is_ac<array_container<T>> : std::true_type { };
      };
      
      template <typename String, template<class> class Allocator, typename T, bool = basic_data_object<String, Allocator>::template is_ac<T>::value>
      struct get_data_object_value
      {
      };
      
      template <typename String, template<class> class Allocator, typename T>
      struct get_data_object_value<String, Allocator, T, true>
      {
        void f() { }
      };
      
      int main()
      {
        get_data_object_value<std::string,std::allocator,std::vector<short>> obj;
        obj.f();
      }
      
      #include <iostream>
      #include <vector>
      
      struct First {
          template <typename T>
          using ArrayType = std::vector<T>;
      };
      
      template <typename T>
      struct Second {
          void go() { std::cout << "General\n"; }
      };
      
      template <typename T>
      struct Second < typename First::template ArrayType<T> > {
          void go() { std::cout << "Specialized\n"; }
      };
      
      int main() {
          Second < std::vector<int> > second;
          second.go();
          return 0;
      }
      
      #include <iostream>
      #include <vector>
      
      template <typename String>
      struct First {
          using ArrayType = std::vector<int>;
      };
      
      template <typename String, typename T>
      struct Second {
          void go() { std::cout << "General\n"; }
      };
      
      template <typename String>
      struct Second < String, typename First<String>::ArrayType > {
          void go() { std::cout << "Specialized\n"; }
      };
      
      
      int main() {
          Second < std::vector<int>, std::vector<int> > second;
          second.go();
          return 0;
      }
      
      #include <iostream>
      #include <vector>
      
      template <typename String>
      struct First {
          template <typename T>
          using ArrayType = std::vector<T>;
      };
      
      template <typename String, typename T>
      struct Second {
          void go() { std::cout << "General\n"; }
      };
      
      template <typename String, typename T>
      struct Second < String, typename First<String>::template ArrayType<T> > {
          void go() { std::cout << "Specialized\n"; }
      };
      
      int main() {
          Second < std::vector<char>, std::vector<int> > second;
          second.go();
          return 0;
      }
      
      template <typename S, template<class> class A, typename T>
      struct get_data_object_value<S,A,std::vector<T,A>>
      { };
      
      template<typename S, template<class> class A>
      struct a_data_object
      {
          template<typename T>
          struct a_container
          { };
      };
      
      template<typename S, template<class> class A, typename T>
      // you can perhaps drop S and A if not needed...
      struct a_container
      { };
      
      template<typename S, template<class> class A, typename T>
      struct a_data_object
      {
          // use a_container<S,A,T>
      };
      
      template <typename S, template<class> class A, typename T>
      struct get_data_object_value<S,A,a_container<S,A,T>>
      { };
      
      template<typename S, template<class> class A, typename T>
      using bdo_container = basic_data_object<S,A>::array_container<T>;
      
      template <typename S, template<class> class A, typename T>
      struct get_data_object_value<S,A,bdo_container<S,A,T>>
      { };
      
      #include <type_traits>
      #include <vector>
      #include <iostream>
      
      template <typename String, template<class> class Allocator>
      struct basic_data_object
      {
          template<typename T>
          using array_container = std::vector<T, Allocator<T>>;
      };
      
      template<typename T, typename String, template<class> class Allocator>
      struct is_basic_data_object_array_container
      /* 
          A trait template that has a `static const bool` member `value` equal to
          `true` if and only if parameter type `T` is a container type
          with `value_type E` s.t. 
              `T` = `basic_data_object<String,Allocator>::array_container<T::E>`
      */ 
      {
          template<typename A> 
          static constexpr bool
          test(std::is_same<
                  A,
                  typename basic_data_object<String,Allocator>::template
                      array_container<typename A::value_type>
                  > *) {
              return std::is_same<
                  A,
                  typename basic_data_object<String,Allocator>::template
                      array_container<typename A::value_type>
                  >::value;
          }
          template<typename A> 
          static constexpr bool test(...) {
              return false;
          }
          static const bool value = test<T>(nullptr);
      };
      
      
      template <
          typename String, 
          template<class> class Allocator,
          typename T,
          bool Select = 
              is_basic_data_object_array_container<T,String,Allocator>::value
      >           
      struct get_data_object_value;
      
      
      template <
          typename String, 
          template<class> class Allocator,
          typename T
      >           
      struct get_data_object_value<
          String,
          Allocator,
          T,
          false
      >
      {
          static void demo() {
              std::cout << "Is NOT a basic_data_object array_container" << std::endl;
          }
      };
      
      template <
          typename String, 
          template<class> class Allocator,
          typename T>
      struct get_data_object_value<
          String, 
          Allocator,
          T,
          true
      >
      {
          static void demo() {
              std::cout << "Is a basic_data_object array_container" << std::endl;
          }
      };
      
      #include <list>
      #include <memory>
      
      using namespace std;
      
      int main(int argc, char **argv)
      {
          get_data_object_value<string,allocator,std::vector<short>>::demo();
          get_data_object_value<string,allocator,std::list<short>>::demo();
          get_data_object_value<string,allocator,short>::demo();
          return 0;
      }
      
      Is a basic_data_object array_container
      Is NOT a basic_data_object array_container
      Is NOT a basic_data_object array_container
      
      template<typename T, typename String, template<class> class Allocator>
      struct is_basic_data_object_array_container
      {
          template<typename A>
          static
          auto test(
              std::is_same<
                  A,
                  typename basic_data_object<String, Allocator>::template
                      array_container<typename A::value_type>
                  > *
              ) ->
                  std::integral_constant<
                      bool,
                      std::is_same<
                          A,
                          typename basic_data_object<String, Allocator>::template
                              array_container<typename A::value_type>
                      >::value
                  >{}
          template<typename A>
          static std::false_type test(...);
          using type = decltype(test<T>(nullptr));
          static const bool value = type::value;
      };