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;
};