std namespace的污染
c++的std namespace是個挺特殊的存在。
假設你想往std命名空間內添加自己的函數,沒錯,這是個很剛需的問題,比如llvm的SmallVector代碼. 會有什麼後果?對此,c++ spec有明確規定:
The behavior of a program is undefined if it adds declarations or definitions to namespace std or to a namespace within namespace std unless otherwise specified. A program may add a template specialization for any standard library template to namespace std only if the declaration depends on a user-defined type and the specialization meets the standard library requirements for the original template and is not explicitly prohibited.
就是說,如果你加一個函數聲明到std namespace裏,如果不是template specialization,那麼這是個undefined行為。
最近碰到一個c++編譯錯誤,簡單說:
namespace std {
template<typename T>
inline void
swap(C<T>& lhs, C<T>& rhs) {
lhs.swap(rhs);
}
}
int main() {
using E = C<unsigned>;
E e1{1u};
E e2{2u};
std::swap(e1, e2);
int a = 10, b = 20;
std::swap(a, b);
}
Linux下gcc-7,gcc-8,gcc-9都可以順利編譯。在某一嵌入式系統上出了問題:
確切的說問題是編譯器升級到gcc8後出現的。
問題的根源在於gcc的升級,2017年5月gcc改變了它的一個extension的支持:Strong Using, Using the GNU Compiler Collection (GCC)
簡單說它試圖做inline namespace類似的實現,不過這個extension的支持已經被拋棄了:
7269|3a5912844d55 (nathan 2017-05-22 7269) for (tree a = attribs; a; a = TREE_CHAIN (a))
7270|3a5912844d55 (nathan 2017-05-22 7270) {
7271|3a5912844d55 (nathan 2017-05-22 7271) tree name = get_attribute_name (a);
7272|3a5912844d55 (nathan 2017-05-22 7272) if (is_attribute_p ("strong", name))
7273|3a5912844d55 (nathan 2017-05-22 7273) {
7274|3a5912844d55 (nathan 2017-05-22 7274) warning (0, "strong using directive no longer supported");
7275|3a5912844d55 (nathan 2017-05-22 7275) if (CP_DECL_CONTEXT (target) == current_namespace)
7276|3a5912844d55 (nathan 2017-05-22 7276) inform (DECL_SOURCE_LOCATION (target),
7277|3a5912844d55 (nathan 2017-05-22 7277) "you may use an inline namespace instead");
7278|3a5912844d55 (nathan 2017-05-22 7278) }
7279|3a5912844d55 (nathan 2017-05-22 7279) else
7280|3a5912844d55 (nathan 2017-05-22 7280) warning (OPT_Wattributes, "%qD attribute directive ignored", name);
7281|3a5912844d55 (nathan 2017-05-22 7281) }
7282|3a5912844d55 (nathan 2017-05-22 7282) }
在STL的實現中,比如swap,並不是簡單的把這些函數或類直接放在std namespace下,而是隱藏在更深的namespace下,比如 std::__1::swap或則std::__2::swap下,同時為了讓這些symbol在std namespace下可以訪問,在c++11之前,gcc這個extension發揮了作用:using namespace __1 __attribute__ ((strong)); 不過有了inline namespace,這個擴展就可以退休了,但假如系統的c++ header仍然在用老的實現就會出問題了,比如這樣:
namespace std {
namespace _LIBCPP_NAMESPACE {
}
using namespace _LIBCPP_NAMESPACE __attribute__((__strong__));
}
總之,在這種老的系統上,一個簡單的workaround是,把namespace std包裝下:
namespace std {
namespace _LIBCPP_NAMESPACE {
。。。swap(...) {}
}}
最後,關於怎麼偏特化std::swap,可以參考下這裡的討論:https://groups.google.com/forum/#!topic/comp.lang.c++.moderated/s5b-2tfc3IE%5B1-25%5D
推薦閱讀: