What Can and Can't I Specialize in the Std Namespace

What can and can't I specialize in the std namespace?

Quoting loosely from the standard:

  • numeric_limits shall not be specialized for non-arithmetic standard types (e.g. complex<T>)

  • "[S]pecializations of shared_ptr shall be CopyConstructible, CopyAssignable, and LessThanComparable [and] convertible to bool."

  • "Specializations of weak_ptr shall be CopyConstructible and CopyAssignable."

  • "[T]emplate specializations [of std::hash] shall meet the requirements of class template hash."

  • Anything in <type_traits>: "The behavior of a program that adds specializations for any of the class templates defined in this subclause is undefined unless otherwise specified." (only some specializations of common_type are explicitly allowed)

  • Locales have certain required specializations.

  • All specializations of istreambuf_iterator shall have a trivial copy constructor, a constexpr default constructor, and a trivial destructor.

  • "The effect of instantiating the class template complex for any type other than float, double, or long double is unspecified." I take it that means that defining such other specializations is pointless.

  • "Specializations and instantiations of the atomic template shall have a deleted copy constructor, a deleted copy assignment operator, and a constexpr value constructor."

  • "The class templates unary_function and binary_function are deprecated. A program shall not declare specializations of these templates."

And of course the overarching clause 17.6.4.2.1, sentence 1 (thanks @sehe and @curiousguy):

The behavior of a C++ 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.

And sentence 2:

The behavior of a C++ program is undefined if it declares

  • an explicit specialization of any member function of a standard library class template, or

  • an explicit specialization of any member function template of a standard library class or class template, or

  • an explicit or partial specialization of any member class template of a standard library class or class template.

A program may explicitly instantiate a template defined in the standard library only if the declaration
depends on the name of a user-defined type and the instantiation meets the standard library requirements
for the original template.

Adding types to the std namespace

No ... part of the point of a namespace is to prevent name collisions on upgrade.

If you add things to the std namespace, then your code might break with the next release of the library if they decide to add something with the same name.

Is it legal to specialize std library functions for a shared_ptr of a user defined type?

Yes, that is legal.

It is even questionably legal to specialize for std::shared_ptr<int> at one point; I don't know if they patched that ambiguity in the standard as a defect or not.

Note that that is a poor implemenation of a hash for global use. First, because it doesn't support null shared pointers. Second, because hashing a shared pointer as always the int value is questionable. It is even dangerous, because if a shared pointer to an int in a container has that int change, you just broke the program.

Consider making your own hasher for these kind of cases.

namespace notstd {
template<class T, class=void>
struct hasher_impl:std::hash<T>{};

namespace adl_helper {
template<class T>
std::size_t hash( T const& t, ... ) {
return ::notstd::hasher_impl<T>{}(t);
}
};
namespace adl_helper2 {
template<class T>
std::size_t hash_helper(T const& t) {
using ::notstd::adl_helper::hash;
return hash(t);
}
}
template<class T>
std::size_t hash(T const& t) {
return ::notstd::adl_helper2::hash_helper(t);
}

struct hasher {
template<class T>
std::size_t operator()(T const& t)const {
return hash(t);
}
};

}

Now this permits 3 points of customization.

First, if you override std::size_t hash(T const&) in the namespace containing T, it picks it up.

Failing that, if you specialize notstd::hasher_impl<T, void> for your type T, it picks it up.

Third, if both of those fail, it invokes std::hash<T>, picking up any specializations.

Then you can do:

std::unordered_set<std::shared_ptr<MyType>, ::notstd::hasher> mySet;

and add:

struct MyType {
MyType(std::string id) : id(id) {}
std::string id;
friend std::size_t hash( MyType const& self) {
return ::notstd::hash(self.id);
}
friend std::size_t hash( std::shared_ptr<MyType> const& self) {
if (!self) return 0;
return ::notstd::hash(*self);
}
};

which should give you a smart hash on on shared_ptr<MyType>.

This keeps the danger that someone changes id on a shared_ptr<MyType> which breaks every container containing the shared_ptr<MyType> in a non-local manner.

Shared state is the devil; consider writing a copy on write pointer if you are really worried about copying these things being expensive.

Why can't I specialize std::tuple_element?

libc++'s entities are actually in std::__1::, an inline namespace inside std. So your own forward declaration template<size_t, class> struct tuple_element; actually declares a different class template, and then the partial specialization blows up due to ambiguity (though the error message is misleading).

Remove the forward declaration and it should work.

extending namespace std via partial template specialization

You might mean [namespace.std]/1:

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
prohibited181.


181) Any library code that instantiates other library templates must be prepared to work adequately with any user-supplied
specialization that meets the minimum requirements of the Standard.

If partial specializations of function templates are ever introduced, this quote would also implicitly cover them (as it doesn't restrict itself on explicit specialization).

Overloading (not specializing) templates in std namespace

It is not possible to define a partial specialization of a function template in C++, so your second example defines an overload not a specialization. Since the standard only allows specializations to be added to namespace std, your overload is illegal.

Is this still true in C++11?

Yes.

Also, does this apply to template classes (like std::hash) too, or just template functions?

You can't overload class templates anyway, you can only overload functions. The same rules apply though, you can only specialize a class template if the specialization depends on a user-defined (meaning non-standard) type.

is there a specific reason for disallowing overloads like in the second case above? (what could potentially break in theory?)

As one example, the implementation might want to take the address of a function but if you've overloaded the function then taking the address could cause an ambiguity and fail to compile, meaning you've just broken valid code in the standard library.

Why is using namespace std; considered bad practice?

Consider two libraries called Foo and Bar:

using namespace foo;
using namespace bar;

Everything works fine, and you can call Blah() from Foo and Quux() from Bar without problems. But one day you upgrade to a new version of Foo 2.0, which now offers a function called Quux(). Now you've got a conflict: Both Foo 2.0 and Bar import Quux() into your global namespace. This is going to take some effort to fix, especially if the function parameters happen to match.

If you had used foo::Blah() and bar::Quux(), then the introduction of foo::Quux() would have been a non-event.

Is specialization of std::to_string for custom types allowed by the C++ standard?


In C++11 and later, is it allowed to specialize std::to_string in the std namespace for custom types?

No. First of all, it is not a template function so you can't specialize it at all.

If you're asking about adding your own overload functions the answer still remains the same.

Documentation snippet from Extending the namespace std:

It is undefined behavior to add declarations or definitions to namespace std or to any namespace nested within std, with a few exceptions noted below

It is allowed to add template specializations for any standard library template to the namespace std only if the declaration depends on a user-defined type and the specialization satisfies all requirements for the original template, except where such specializations are prohibited.


In practice everything will probably work just fine but strictly speaking the standard says there is no guarantee of what will happen.


Edit: I don't have access to the official standard so the following is from the free working draft (N4296):

17.6.4.2 Namespace use

17.6.4.2.1 Namespace std

  1. The behavior of a C++ 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.181
  2. The behavior of a C++ program is undefined if it declares

    2.1 — an explicit specialization of any member function of a standard library class template, or

    2.2 — an explicit specialization of any member function template of a standard library class or class template,
    or

    2.3 — an explicit or partial specialization of any member class template of a standard library class or class
    template.

    A program may explicitly instantiate a template defined in the standard library only if the declaration
    depends on the name of a user-defined type and the instantiation meets the standard library requirements
    for the original template.

  3. A translation unit shall not declare namespace std to be an inline namespace (7.3.1).


Related Topics



Leave a reply



Submit