部分模板专门化的意外结果
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 );
}
请注意,为了演示而减少了代码,在实际代码中,我将使用variadic函数中的迭代器来遍历输入容器,并为容器的每个元素调用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 );
现在只需要专门化值类型。
类型定义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链接)。
- 尝试将字符串/字符转换为整数会产生意外结果
- RapidXML - 代码创建意外结果
- 类中静态函数C++意外结果
- 指针数组中的意外结果
- 使用指针访问数组元素时出现意外结果
- 使用 sprintf 和 %g 将双精度转换为字符串的意外结果
- C++:比较运算符>和字符串文本的意外结果
- 具有意外结果的 C++ 闭包
- yaml-cpp 比较的意外结果
- 每次都出现意外结果
- 在 Qt 中解析嵌套的 JSON 时出现意外结果(数组不存在)
- 如何避免 std::abs 的意外结果?
- 使用嵌套 if 语句的意外结果
- A ^= B ^= A ^= B;C# Visual Studio 中的意外结果
- 逻辑错误,我将获得意外结果
- 在 c++ 中使用异步的意外结果
- 从函数的返回值将元素C++存储到 std::vector 中时出现意外结果
- 执行递增和递减时"cout"链接会产生意外结果
- OpenCL - 内核方法返回意外结果
- C++使用 std::get_time 解析 YYMMDD ISO 8601 日期字符串会得到意外结果