1

I've got a bunch of my own objects which I've dutifully put inside my own namespace:

namespace my { struct foo final {}; /* etc. */}

Where should I put non-member non-friend functions (i.e., "global" utility routines) which take my types as parameters? Should I also put them in the my namespace

namespace my { extern void f(const foo&); }

Or, is there an upside (or downside) to put them in the global namespace

extern void f(const my::foo&);

In either case, the argument to f is my::foo, so does it matter whether the function itself is actually named ::f() or my::f()?

Edit: note that I'm specifically not looking for "I like global" or "I like in the namespace" (or similar). Rather, I'm seeking specific technical reasons to prefer one approach over the other (assuming such differences actually exist). From a comment, it sounds like one (the?) factor to consider may be ADL behavior; are there others?

Community
  • 1
  • 1
Ðаn
  • 10,934
  • 11
  • 59
  • 95
  • This is a principally Opinion Based question, and is thus off-topic for this site. You might consider taking your question to [softwareengineering.stackexchange.com](http://softwareengineering.stackexchange.com/) – Xirema Feb 21 '17 at 20:28
  • 3
    Putting them in the same namespace as the main argument type lets ADL find them, which is convenient. Due to the arguments there is no /technical/ problem with having them in the global namespace. But when name resolution fails these irrelevant functions might effectively produce noise in the diagnostics. – Cheers and hth. - Alf Feb 21 '17 at 20:29
  • 1
    @Xirema when referring other sites, it is often helpful to point that [cross-posting is frowned upon](http://meta.stackexchange.com/tags/cross-posting/info). FWIW opinion based questions aren't welcome there either, please avoid misleading people with such ideas - see **[What goes on Software Engineering (previously known as Programmers)? A guide for Stack Overflow](http://meta.softwareengineering.stackexchange.com/q/7182/31260)** – gnat Feb 21 '17 at 21:21
  • @gnat I read that entire post and I think I understand *less* about what kind of questions belong on that site than I did before. They say they're okay with "subjective" questions but don't like "opinion" questions.... What? That makes literally no sense. – Xirema Feb 21 '17 at 21:44
  • 2
    @Xirema "primarily opinion-based" == "your guess is as good as mine, flip a coin" while "good subjective" == "there are two good solutions and either will work, which should I choose to achieve objective X?" –  Feb 22 '17 at 05:01

2 Answers2

1

Putting operations tightly coupled to a type in the same namespace as the type permits Argument Dependent Lookup or Koenig lookup to work.

Polluting the global namespace is often a bad idea; you only get one global namespace, and if your code doesn't play with what someone else put there, there are few solutions.

For example, if you have a type or function named CString in the global namespace and you try to include and use MFC code which injects a CString into the global namespace, you are screwed.

Placing code in a namespace means you pollute the global namespace with one token, instead of one per function and type name (etc).

A second thing to consider is if your types are template generated, placing a friend function within the template results in an ADL-discoverable non-template function which is found when interacting with instances of the class instances of the template.

namespace my {
  template<class T>
  struct foo {
    friend foo operator+( foo lhs, foo rhs ) { return {lhs.x+rhs.x}; }
    friend foo add( foo lhs, foo rhs ) { return {lhs.x+rhs.x}; }
    int x;
  };
}

now my::foo f0{0},f1{1}; auto f2 = add(f0, f1); works, as does auto f3=f0+f1;.

Both add and operator+ here are not templates, which means if we have a type that converts-to-foo, add(f0, converts_to_foo) also works.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • @Dan No, not "have". This is a technique I personally call Koenig Operators, where you use free friend functions like above to augment a type's interface in an ADL friendly manner; such names are in the enclosing namespace (sortof). – Yakk - Adam Nevraumont Feb 21 '17 at 20:48
1

It depends. Sometimes you have a function that takes two arguments from unrelated namespaces (e.g. in I/O or serialization), it could reside in either of those (but not in the global namespace!)

// foo can be either in N1 or N2, but not in global
namespace N1 { auto foo(A&, N2::B const&) }
namespace N2 { auto foo(N1::A&, B const&) } 

Or you have an algorithm that is supposed to work for generic arguments (e.g. input and output ranges) you would put that function in its own namespace

// algo should probably be in its own namespace N
namespace N {
    template<class R1, class R2>
    auto algo(R1 const& in, R2& out)
}

Calling such an algorithm is best done without relying on ADL, i.e. as N::algo(rin, rout).

TemplateRex
  • 69,038
  • 19
  • 164
  • 304