[英]Unexpected result with partial template specialisation
我在使用个人格式库时遇到了一些意外的结果。 我已将代码简化为可以在下面或在coliru上找到的列表。
#include <iostream>
#include <map>
#include <utility>
#include <string>
template <typename T>
struct executioner {
inline static void exec( const T & ){
std::cout << "generic" << std::endl;
}
};
template <typename T1, typename T2>
struct executioner<std::pair<T1, T2> > {
inline static void exec( const std::pair<T1, T2> & t ){
std::cout << "pair" << std::endl;
executioner<T1>::exec( t.first );
executioner<T2>::exec( t.second );
}
};
template <template <typename ...> class Container,
typename ... Ts>
struct executioner<Container<Ts ...> > {
inline static void exec( const Container<Ts ...> & c ){
std::cout << "cont" << std::endl;
auto it = c.begin();
typedef decltype( * it ) ContainedType;
executioner<ContainedType>::exec( * it );
}
};
template <typename T> void execute( const T & t ){
executioner<T>::exec( t );
}
int main(){
std::map<int,std::string> aMap = { { 0, "zero" }, { 1, "one" }, { 2, "two" } };
execute( aMap );
}
请注意,为演示起见,已减少了代码,在实际代码中,我将使用可变参数函数中的迭代器来遍历输入容器,并调用executioner<ContainedType>::exec( * it );
对于容器的每个元素。
执行此代码,我期望以下输出:
续
对
通用
令我惊讶的是,未使用std::pair
的专业化,实际输出为:
续
通用
我非常怀疑这是一个编译器错误(因为gcc 4.9
和clang 3.2
都发生了),因此我问
我没想到有什么收获?
将可变参数模板代码更改为以下内容:
template <template <typename ...> class Container,
typename ... Ts>
struct executioner<Container<Ts ...> > {
inline static void exec(const Container<Ts ...> & c){
std::cout << "cont" << std::endl;
auto it = c.begin();
executioner<typename Container<Ts...>::value_type>::exec(*it);
}
};
并且您的代码将按预期工作。
我将通过一个示例来说明为什么decltype
无法按预期工作:
template <template <typename ...> class Container,
typename ... Ts>
struct executioner<Container<Ts ...> > {
inline static void exec( const Container<Ts ...> & c ){
std::cout << "cont" << std::endl;
auto it = c.begin();
typedef decltype( (*it) ) ContainedType;
if(std::is_same<decltype(it->first), const int>::value) {
std::cout << "IS CONST !!!" << std::endl;
}
executioner<ContainedType>::exec( * it );
}
};
如果使用上面的代码替换,您将看到条件std::is_same<decltype(it->first), const int>::value
为true
。 这意味着*it
的类型为std::pair<const int, std::basic_string<...>>
而不是std::pair<int, std::basic_string<...>>
。 因此,您的专业“失败”。
好吧,一切都回到我身边了:)
正如ildjarn所暗示的,您可以使用std::remove_reference
删除引用。 我还剥离了const
限定符(请参阅此线程 )。 我的便利结构如下所示:
template <typename T>
struct to_value
{
typedef typename std::remove_const<
typename std::remove_reference< T >::type
>::type type;
};
您这样称呼它:
typedef decltype( *it ) ContainedType;
executioner< typename to_value<ContainedType>::type >::exec( *it );
现在,您只需要专注于值类型。 这就是全部 。
typedef ContainedType
不是std::pair<int, std::string>
。 它是const std::pair<const int, std::string>&
。 这就是为什么您的第一个部分专业化不匹配的原因。 您可以这样修改它:
template <typename T1, typename T2>
struct executioner<const std::pair<T1, T2>&> {
inline static void exec( const std::pair<T1, T2> & t ) {
std::cout << "pair" << std::endl;
executioner<T1>::exec( t.first );
executioner<T2>::exec( t.second );
}
};
和它匹配( coliru链接 )。
或者,您可以使用容器的value_type
,而不是decltype(*it)
:
typedef typename Container<Ts...>::value_type ContainedType;
在后一种情况下, ContainedType
是预期的std::pair<const int, std::string>
( coliru链接 )。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.