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,可以參考下這裡的討論:groups.google.com/forum


推薦閱讀:
相關文章