16

In this article on defining your own extensions to ::std::error_code the author recommends this code:

namespace std
{
  template <>
  struct is_error_code_enum<http_error>
    : public true_type {};
}

in order to enable conversions from your own error constants to the system error type.

Is this reasonable? It always makes me nervous to put things into the std namespace. Is there a better way of accomplishing the goal? Failing all that, is there a part of the standard that says this is always OK to do?

Omnifarious
  • 54,333
  • 19
  • 131
  • 194

2 Answers2

24

Yep, specializations (for user-defined types) of existing std types are the only thing you're allowed to put in the std namespace, as long as the specialization meets the requirements for the original template.

See 17.6.4.2.1 in the C++0x draft.

New types, function overloads and anything else, of course, is forbidden. But specializations of existing templates is allowed.

jalf
  • 243,077
  • 51
  • 345
  • 550
  • 1
    You have the better answer, but @Steve314 includes the important detail that in some cases you absolutely have to do this. I will likely wait a few hours (just on general principles) and then probably accept this one. – Omnifarious Aug 05 '11 at 10:15
8

This isn't just OK - it's essential in some cases. What you shouldn't do is to define entirely new functions/classes/templates within std.

std::swap in particular is a common and simple thing to specialise. Some classes need to do this to allow efficient swaps, basically swapping private references to exchange internals rather than using the default temporary-and-assignments implementation.

EDIT

The comment by ildjarn mentions ADL - Argument Dependent name Lookup. The Wikipedia page specifically mentions std::swap in the "Interfaces" and "Criticism" section.

Section 13.5.2 of Stroustrup (Special Edition) includes an example of specialization of std::swap. Quoting from that...

These specializations of less() and swap() are used in the standard library (16.3.9, 20.3.16). In addition, they are examples of widely applicable techniques.

I've always read that as indicating that specialization of std::swap was the right thing to do, and I've never worried enough about ADL to question that, but it's possible there's a gap between "used in the standard library" and "widely applicable techniques" - that this technique shouldn't be used to specialise std::swap to handle types that aren't in std.

It's also possible that there is a style issue that hadn't been decided back when the special edition was first published. AFAIK, Stroustrup has added some extra appendices and applied some errata, but otherwise not substantially revised the content.

Based on the Wikipedia page, there is a potential problem with mixing the addition of specialisations and ADL - sometimes you can get ambiguity preventing any lookup. This only happens if you mix the two techniques, and ADL is know for leading to semantic issues anyway. But that argument only leads to "don't use ADL at all", but ADL does exist for a reason.

Well, yes, ADL exists for a reason - so that non-member functions and operators that work with a time are visible along with the type. But std::swap isn't associated with one particular type - it's generic, with only particular specializations being associated with particular types. If you want std::swap to be visible, you want the std namespace. ADL isn't necessary to make that work and, as the Wikipedia page points out, there are criticisms of ADL.

What this means is, basically, that I don't know. I have my rationalisations. They don't necessarily agree with more widespread style rules. Certainly this comment proves that it's not essential to specialise std::swap - you can supply your own separate swap and rely on ADL instead. Maybe that's preferred.

I'll probably come back and edit again after I've checked.

  • 2
    `std::swap` should **not** be specialized or overloaded -- instead, one should put a `swap` free function in the same namespace as the type being swapped, and let ADL do the rest. – ildjarn Aug 05 '11 at 19:30
  • @ildjarn - thanks - I'm not sure you're right yet, but see the edit. –  Aug 05 '11 at 22:39
  • ADL was widely broken in major compilers for quite some time, so specializing `std::swap` became standard practice. However, with modern, more standard-conformant compilers, that workaround is no longer necessary. – ildjarn Aug 05 '11 at 23:42
  • The problem with relying on ADL is that a lot of code explicitly calls `std::swap`, rather than `swap`. I don't really see the argument in favor of relying on ADL for `swap`. I ca nsee it working most of the time, but I don't see why it'd be a *superior* option – jalf Aug 06 '11 at 16:57
  • @jalf - I think my problem is that I see overloading not as selecting from many different functions, but as compile-time dispatch - selecting from multiple implementations of one function. And I think of the namespace as part of the name of the function. From that point of view, relying on ADL in this case is horrible - `whatever::swap` and `std::swap` are conceptually two unrelated functions, not just different implementations. The trouble with that is it may just mean I have the wrong mental model. –  Aug 06 '11 at 17:31
  • @jalf - Your comment is interesting, as it shows a roughly opposite problem to the one in the Wikipedia page - whether ADT seems superior or specialisation within `std` seems superior may depend primarily on whether people tend to use `using namespace std;`. If you use `using`, you may get the ambiguity issue (an `std::swap` and an `other::swap` both equally valid). If you don't, you may get the nothing-found issue (`other::swap` not found because you explicitly called `std::swap` rather than just `swap`). Using ADT seems to force the use of `using namespace`. –  Aug 06 '11 at 17:36
  • @Steve: I agree with you on ADL, and I don't think it's "the wrong mental model" per se. – jalf Aug 06 '11 at 22:57
  • @Steve314 : There cannot be any ambiguity in that scenario, because overload resolution always prefers non-templates over templates. And the proper thing to do for `swap` in particular is a using declaration rather than a using directive. – ildjarn Aug 09 '11 at 17:05