libstdc++ 4.6 type_traits 的一个bug

如果你的编译环境和我一样,然后又在用C++11的时候,不小心直接或者间接用到了<chrono>这个头文件,应该就会遇到这个bug。

完整的错误信息如下:

|| In file included from /usr/lib/gcc/x86_64-linux-gnu/4.6/../../../../include/c++/4.6/thread:37:
/usr/include/c++/4.6/chrono|240 col 10| error: cannot cast from lvalue of type 'const long' to rvalue reference type 'rep' (aka 'long &&'); types are not compatible
||           : __r(static_cast(__rep)) { }
||                 ^~~~~~~~~~~~~~~~~~~~~~~
/usr/include/c++/4.6/chrono|128 col 13| note: in instantiation of function template specialization 'std::chrono::duration >::duration' requested here
||             return _ToDur(static_cast(__d.count()));
||                    ^
/usr/include/c++/4.6/chrono|182 col 9| note: in instantiation of function template specialization 'std::chrono::__duration_cast_impl<std::chrono::duration >, std::ratio, long &&, true, true>::__cast >' requested here
||         return __dc::__cast(__d);
||                ^
/usr/include/c++/4.6/chrono|247 col 10| note: in instantiation of function template specialization 'std::chrono::duration_cast<std::chrono::duration >, long, std::ratio >' requested here
||           : __r(duration_cast(__d).count()) { }
||                 ^
/usr/include/c++/4.6/chrono|466 col 9| note: in instantiation of function template specialization 'std::chrono::duration >::duration, void>' requested here
||         return __ct(__lhs).count() < __ct(__rhs).count();
||                ^
/usr/include/c++/4.6/chrono|667 col 7| note: in instantiation of function template specialization 'std::chrono::operator<, long, std::ratio >' requested here
||                     < system_clock::duration::zero(),
||                     ^
/usr/include/c++/4.6/chrono|128 col 13| error: call to implicitly-deleted copy constructor of 'std::chrono::duration >'
||             return _ToDur(static_cast(__d.count()));
||                    ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/include/c++/4.6/chrono|182 col 9| note: in instantiation of function template specialization 'std::chrono::__duration_cast_impl<std::chrono::duration >, std::ratio, long &&, true, true>::__cast >' requested here
||         return __dc::__cast(__d);
||                ^
/usr/include/c++/4.6/chrono|247 col 10| note: in instantiation of function template specialization 'std::chrono::duration_cast<std::chrono::duration >, long, std::ratio >' requested here
||           : __r(duration_cast(__d).count()) { }
||                 ^
/usr/include/c++/4.6/chrono|466 col 9| note: in instantiation of function template specialization 'std::chrono::duration >::duration, void>' requested here
||         return __ct(__lhs).count() < __ct(__rhs).count();
||                ^
/usr/include/c++/4.6/chrono|667 col 7| note: in instantiation of function template specialization 'std::chrono::operator<, long, std::ratio >' requested here
||                     < system_clock::duration::zero(),
||                     ^
/usr/include/c++/4.6/chrono|233 col 12| note: explicitly defaulted function was implicitly deleted here
||         constexpr duration(const duration&) = default;
||                   ^
/usr/include/c++/4.6/chrono|349 col 6| note: copy constructor of 'duration >' is implicitly deleted because field '__r' is of rvalue reference type 'rep' (aka 'long &&')
||         rep __r;
||             ^
/usr/include/c++/4.6/chrono|182 col 9| error: call to implicitly-deleted copy constructor of 'typename enable_if<__is_duration<duration > >::value, duration > >::type' (aka 'std::chrono::duration >')
||         return __dc::__cast(__d);
||                ^~~~~~~~~~~~~~~~~
/usr/include/c++/4.6/chrono|247 col 10| note: in instantiation of function template specialization 'std::chrono::duration_cast<std::chrono::duration >, long, std::ratio >' requested here
||           : __r(duration_cast(__d).count()) { }
||                 ^
/usr/include/c++/4.6/chrono|466 col 9| note: in instantiation of function template specialization 'std::chrono::duration >::duration, void>' requested here
||         return __ct(__lhs).count() < __ct(__rhs).count();
||                ^
/usr/include/c++/4.6/chrono|667 col 7| note: in instantiation of function template specialization 'std::chrono::operator<, long, std::ratio >' requested here
||                     < system_clock::duration::zero(),
||                     ^
/usr/include/c++/4.6/chrono|233 col 12| note: explicitly defaulted function was implicitly deleted here
||         constexpr duration(const duration&) = default;
||                   ^
/usr/include/c++/4.6/chrono|349 col 6| note: copy constructor of 'duration >' is implicitly deleted because field '__r' is of rvalue reference type 'rep' (aka 'long &&')
||         rep __r;
||             ^
/usr/include/c++/4.6/chrono|255 col 11| error: rvalue reference to type 'long' cannot bind to lvalue of type 'long'
||         { return __r; }
||                  ^~~
/usr/include/c++/4.6/chrono|247 col 39| note: in instantiation of member function 'std::chrono::duration >::count' requested here
||           : __r(duration_cast(__d).count()) { }
||                                              ^
/usr/include/c++/4.6/chrono|466 col 9| note: in instantiation of function template specialization 'std::chrono::duration >::duration, void>' requested here
||         return __ct(__lhs).count() < __ct(__rhs).count();
||                ^
/usr/include/c++/4.6/chrono|667 col 7| note: in instantiation of function template specialization 'std::chrono::operator<, long, std::ratio >' requested here
||                     < system_clock::duration::zero(),
||                     ^
/usr/include/c++/4.6/chrono|666 col 21| error: static_assert expression is not an integral constant expression
||       static_assert(system_clock::duration::min()
||                     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/include/c++/4.6/chrono|666 col 21| note: undefined function 'operator<, long, std::ratio >' cannot be used in a constant expression
/usr/include/c++/4.6/chrono|460 col 7| note: declared here
||       operator& __lhs,
||       ^
/usr/include/c++/4.6/chrono|141 col 40| error: cannot cast from lvalue of type 'const intmax_t' (aka 'const long') to rvalue reference type 'long &&'; types are not compatible
||               static_cast(__d.count()) / static_cast(_CF::den)));
||                                               ^~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/include/c++/4.6/chrono|182 col 9| note: in instantiation of function template specialization 'std::chrono::__duration_cast_impl<std::chrono::duration >, std::ratio, long &&, true, false>::__cast >' requested here
||         return __dc::__cast(__d);
||                ^
/usr/include/c++/4.6/chrono|679 col 21| note: in instantiation of function template specialization 'std::chrono::duration_cast<std::chrono::duration >, long, std::ratio >' requested here
||         return std::time_t(duration_cast
||                            ^
/usr/include/c++/4.6/chrono|154 col 40| error: cannot cast from lvalue of type 'const intmax_t' (aka 'const long') to rvalue reference type 'long &&'; types are not compatible
||               static_cast(__d.count()) * static_cast(_CF::num)));
||                                               ^~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/include/c++/4.6/chrono|182 col 9| note: in instantiation of function template specialization 'std::chrono::__duration_cast_impl<std::chrono::duration >, std::ratio, long &&, false, true>::__cast >' requested here
||         return __dc::__cast(__d);
||                ^
/usr/include/c++/4.6/chrono|577 col 22| note: in instantiation of function template specialization 'std::chrono::duration_cast<std::chrono::duration >, long, std::ratio >' requested here
||         return __time_point(duration_cast(__t.time_since_epoch()));
||                             ^
/usr/include/c++/4.6/chrono|687 col 9| note: in instantiation of function template specialization 'std::chrono::time_point_cast<std::chrono::duration >, std::chrono::system_clock, std::chrono::duration > >' requested here
||         return time_point_cast
||                ^
|| 7 errors generated.

Google搜到了这个帖子 http://urfoex.blogspot.com/2012/06/c11-fixing-bug-to-use-chrono-with-clang.html
和我的环境略有不同,但是出错的地方是一样的。

我自己的环境是

Ubuntu 12.04, clang 3.1, libstdc++ 4.6

解决方法是改

/usr/include/c++/4.6/type_traits

这个文件的第1113行

{ typedef decltype(true ? declval<_Tp>() : declval<_Up>()) type; };

改成

{ typedef typename decay() : declval<_Up>())>::type type; }

出错的原因,大概是因为没有正确的做类型转换。decay这个函数我从没用过,看文档是C++11引入的函数,用做类型转换。对此不理解,不能在这里误导人,文档原文如下

Applies lvalue-to-rvalue, array-to-pointer, and function-to-pointer implicit conversions to the type T, removes cv-qualifiers, and defines the resulting type as the member typedef type. This is the type conversion applied to all function arguments when passed by value.

总之,把那行改掉再重新编译自己的工程就没有问题了。

如果你是和我一样没有root权限去改/usr/include下的文件,也可以这么做:
1. 把/usr/include/c++/4.6/type_traits拷贝到自己的目录下面,比如

$cp /usr/include/c++/4.6/type_traits $HOME/local/include/c++/4.6

2. 改自己目录下的版本,然后编译的时候把这个目录放到编译选项里,clang会优先使用这个目录下的版本

$clang++ blahblah.cpp -I$HOME/local/include/c++/4.6/

好吧,虽然没有完全搞清楚这个bug是怎么回事,但是解决掉一大堆报错的感觉挺好 :]