Dra*_*rax 19 c++ templates specialization c++11
This is probably only a syntax problem.
So i have this template class :
template <typename String, template<class> class Allocator>
class basic_data_object
{
template<typename T>
using array_container = std::vector<T, Allocator<T>>;
};
Run Code Online (Sandbox Code Playgroud)
And another one :
template <typename String, template<class> class Allocator, typename T>
struct get_data_object_value
{
};
Run Code Online (Sandbox Code Playgroud)
Now i want to specialize the second one's T parameter with the first one's inner typedef array_container for any given type.
template <typename String, template<class> class Allocator, typename T>
struct get_data_object_value
<String, Allocator,
typename basic_data_object<String, Allocator>::template array_container<T>>
{
};
Run Code Online (Sandbox Code Playgroud)
But this specialization doesn't seem to be matched when i pass an std::vector as the last parameter.
If i create a temporary hard coded typedef:
typedef basic_data_object<std::string, std::allocator<std::string>> data_object;
Run Code Online (Sandbox Code Playgroud)
And use it for the specialization, everything works :
template <typename String, template<class> class Allocator, typename T>
struct get_data_object_value
<String, Allocator,
data_object::template array_container<T>>
{
};
Run Code Online (Sandbox Code Playgroud)
What did i miss ? :)
Alternatively what is the best (smallest / cleanest) way to make this work ?
C++标准在[temp.class.spec.match]第2段中说:
如果可以从实际模板参数列表推导出部分特化的模板参数,则部分特化匹配给定的实际模板参数列表(14.8.2).
14.8.2是[temp.arg.deduct],即描述函数模板的模板参数推导的子句.
如果您修改代码以使用类似的函数模板并尝试调用它,您将看到无法推断出参数:
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>>{});
}
Run Code Online (Sandbox Code Playgroud)
(我删除了Allocator参数,因为没有办法将模板模板参数作为函数参数传递,并且在basic_data_object类型中它是一个非推导的上下文,我不相信它会影响结果.)
铿锵和海湾合作委员会都表示他们不能T在这里演绎.因此,部分特化与用作模板参数的相同类型不匹配.
所以我还没有真正回答这个问题,只是澄清了原因在于模板参数推导的规则,并且在函数模板中显示了与推导的等价.
在14.8.2.5 [temp.deduct.type]中,我们得到了一个阻止扣除的非推断上下文列表,以及第6段中的以下规则:
如果以包含非推导上下文的方式指定类型名称,则包含该类型名称的所有类型也是非推断的.
由于basic_data_object<String, Allocator>处于非推导的上下文(它是嵌套名称说明符,即之前出现::),这意味着该类型T也是非推导的,这正是Clang和GCC告诉我们的.
使用临时硬编码的typedef,没有非推断的上下文,因此T使用deduction_test函数模板推断成功:
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
}
Run Code Online (Sandbox Code Playgroud)
因此,相应地,您的类模板部分特化可以在使用该类型时进行匹配.
我没有看到一种方法可以在不改变定义的情况下使其工作get_data_object_value,但如果这是一个选项,你可以删除推断array_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();
}
Run Code Online (Sandbox Code Playgroud)
如果您需要多个类模板部分特化,则无法真正扩展,因为您需要bool使用默认参数添加多个模板参数.
出于某种原因,问题似乎源于双层模板.我将告诉你检查下面的3个测试用例,它们很简单:
First:按预期工作First一个模板,但内部类型是普通的:按预期工作First和内部类型模板:编译但输出是意外的注意:模板模板参数Allocator无法重现问题,所以我把它留了出来.
注意:GCC(ideone的版本,4.8.1我相信)和Clang(Coliru版本,3.4)编译代码,但产生相同的令人困惑的结果
从以上3个例子中,我推断:
因此,要么问题比目前的提示更加毛茸茸会使我们相信或者gcc和Clang都有错误.
编辑:感谢Jonathan Wakely,他耐心地教育了我,我终于理解了与此案相关的标准措辞及其应用方式.我现在试着用我自己的话来解释这个问题.请参阅Jonathan的答案,了解确切的标准报价(全部都在[temp.deduct.type]中)
(*)似乎有可能从可用的候选人中找到"共同类型"......但这并不重要.
现在我们可以将它应用于前面的示例:
1)T存在单个模板参数:
std::vector<int>反对typename First::template ArrayType<T>(这是std::vector<T>),我们得到d 0:{ T -> int }{ T -> int },因此T被推断为int2)String存在单个模板参数
std::vector<int>反对String,我们得到d 0:{ String -> std::vector<int> }std::vector<int>对typename First<String>::ArrayType我们打非可推断上下文(许多值String可能适合),我们得到d 1:{}{ String -> std::vector<int> },因此String被推断为std::vector<int>3)两个模板参数String和T存在
std::vector<char>反对String,我们得到d 0:{ String -> std::vector<char> }std::vector<int>对typename First<String>::template ArrayType<T>我们打非抵扣范围内,我们得到d 1:{}{ String -> std::vector<char> },这是一个不完整的字典(T缺席),演绎失败我必须承认,我还没有考虑过这些论点是彼此独立解决的,因此,在最后一种情况下,当计算D 1时,编译器无法利用D 0已经推导出值的事实String.然而,为什么以这种方式完成它可能是一个完整的问题.
没有外部模板,它可以工作,就像打印"Specialized"一样:
#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;
}
Run Code Online (Sandbox Code Playgroud)
没有内部模板,它可以正常工作,因为它打印"Specialized":
#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;
}
Run Code Online (Sandbox Code Playgroud)
两者都失败了,就像打印"General"一样:
#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;
}
Run Code Online (Sandbox Code Playgroud)
Jonathan Wakely的答案给出了代码不起作用的原因.
我的回答告诉你如何解决问题.
在您的示例中,您想要专门化的容器类型是在外部定义的,basic_data_object因此您当然可以直接在您的专业化中使用它:
template <typename S, template<class> class A, typename T>
struct get_data_object_value<S,A,std::vector<T,A>>
{ };
Run Code Online (Sandbox Code Playgroud)
这绝对符合标准,适用于所有编译器.
在定义类型的情况下basic_data_object,您可以将其移出类.
示例:而不是
template<typename S, template<class> class A>
struct a_data_object
{
template<typename T>
struct a_container
{ };
};
Run Code Online (Sandbox Code Playgroud)
写这个:
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>
};
Run Code Online (Sandbox Code Playgroud)
现在你可以专注于:
template <typename S, template<class> class A, typename T>
struct get_data_object_value<S,A,a_container<S,A,T>>
{ };
Run Code Online (Sandbox Code Playgroud)
注意:下一个"解决方案"显然是GCC 4.8.1的错误.
如果容器仅在封闭模板中定义且无法移出,则可以执行以下操作:
获取容器类型basic_data_object:
template<typename S, template<class> class A, typename T>
using bdo_container = basic_data_object<S,A>::array_container<T>;
Run Code Online (Sandbox Code Playgroud)为此类型编写专门化:
template <typename S, template<class> class A, typename T>
struct get_data_object_value<S,A,bdo_container<S,A,T>>
{ };
Run Code Online (Sandbox Code Playgroud)| 归档时间: |
|
| 查看次数: |
1530 次 |
| 最近记录: |