Why (or when) does a template bring in its namespace? - c++

Here's an example:
#define MAKE_IT_WORK false
namespace Bob { // Bob's project namespace
struct DeviceFrequency {};
extern void debugf(const char* fmt, ...);
} // namespace Bob
namespace SSN { // Super-Secret Namespace
namespace debugging {
extern int ssn_debug;
extern void debugf(const char* fmt, ...);
} // namespace debugging
} // namespace SSN
namespace SSN::signals { // Super-Secret Namespace, project signals
template<typename Coder> // In the example, this imports Bob's namespace
class Frequency {
public:
Frequency( void )
{ using namespace ::SSN::debugging; // Why isn't this enough??
using ::SSN::debugging::debugf; // Or this??
if( ssn_debug )
#if MAKE_IT_WORK
::SSN::debugging:: // How can a developer predict that this is needed??
#endif
debugf("Frequency(%p,%zd)::Frequency\n", this, sizeof(*this));
}
}; // class Frequency
} // namespace SSN::signals
struct Controller {
SSN::signals::Frequency<Bob::DeviceFrequency> bobcon;
Controller( void ) : bobcon() {}
}; // class Controller
In this example, Bob duplicates the debugf function because he doesn't want to have to bring the entire SSN namespace into his private one and doesn't want to be bothered with fully qualifying it every time he uses it.
The SSN developer didn't realize that a template could also import its namespace (until it happened), apparently bringing ADL lookup into play. Although ADL lookup states that using statements in namespace are ignored, why it comes into play at all doesn't make sense. It seems way too easy for one namespace to unintentionally pollute another, and way too hard to predict where this might someday occur in inline code.
It looks like (at least) whenever templates are used within a namespace, every namespace function reference must be fully qualified because when using templates you can't predict when a name conflict might otherwise occur. Is this correct? If so, is there any way to avoid all that extra name qualifier typing that seems to be required? Is this exposure limited to templates, or is all imported inline code somehow similarly vulnerable?
Compiling (Dirty.cpp) using gcc versions 10.2.0 and 10.2.1 (Red Hat 10.2.1-5), I got the following messages:
make dirty
c++ -o Dirty.o -c S/Dirty.cpp -D_CC_GCC -D_OS_BSD -D_HW_X86 -D_OS_LINUX -IS -IH -g -O3 -finline->functions -std=gnu++17 -Wall -Wextra -Wmissing-declarations -Wswitch-default -Werror
S/Dirty.cpp: In instantiation of ‘SSN::signals::Frequency<Coder>::Frequency() [with Coder = Bob::DeviceFrequency]’:
S/Dirty.cpp:146:32: required from here
S/Dirty.cpp:139:12: error: call of overloaded ‘debugf(const char [30], SSN::signals::Frequency<Bob::DeviceFrequency>*, long unsigned int)’ is ambiguous
139 | debugf("Frequency(%p,%zd)::Frequency\n", this, sizeof(*this));
| ~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
S/Dirty.cpp:124:13: note: candidate: ‘void SSN::debugging::debugf(const char*, ...)’
124 | extern void debugf(const char* fmt, ...);
| ^~~~~~
S/Dirty.cpp:117:13: note: candidate: ‘void Bob::debugf(const char*, ...)’
117 | extern void debugf(const char* fmt, ...);
|
(Edited: The example has been restored to the original, now as reformatted by #1201ProgramAlarm. A modified version appears that tests examples follows in the partial answer below.)

For a template, ADL includes the namespaces and entities associated with the
types of the template arguments provided for template type parameters. Because the call to debugf includes this as a parameter, ADL will include namespace Bob because the template is instantiated with Bob::DeviceFrequency.

Updated example:
// Last change: 2020/10/30 11:15 EDT
#define MAKE_IT_WORK true // Modified (Example compiles)
namespace Bob { // Bob's project namespace
extern double thing; // (Added for answer testing)
struct Bar { int foo; }; // (Added for answer testing)
struct Foo { int bar; }; // (Added for answer testing)
extern Foo bar; // (Added for answer testing)
struct DeviceFrequency { double f; }; // (Modified: added "double f;")
extern void debugf(const char* fmt, ...);
static inline void function(DeviceFrequency& E) // (??TRICKY inference??)
{ debugf("default function, E: %f\n", E); }
} // namespace Bob
namespace SSN { // Super-Secret Namespace
namespace debugging {
extern int ssn_debug;
extern void debugf(const char* fmt, ...);
} // namespace debugging
} // namespace SSN
namespace SSN::signals { // Super-Secret Namespace, project signals
template<typename Coder>
class Frequency {
// Note: The Function type references the template indirectly, and has
// no purpose other than to make sure that a <Coder> function handler
// receives a <Coder&> parameter. ADL doesn't care. It brings in the
// Coder's namespace looking for functions to override.
typedef std::function<void(Coder&)> Function; // (Added) Another "gotcha"
const Function function; // This indirectly references Coder&, thus Bob
double thing= 543.21; // (Added) Duplicates name in namespace Bob
double f= 123.45; // (Added) Duplicates name in Bob::DeviceFrequency
Bob::Foo bar{876}; // (Added) Uses Bob namespace
struct Bar { int foo= 0x0F00; } foo; // (Added) Duplicates a Bob struct name
struct Derived : public Bob::Foo {}; // (Added) Uses Bob namespace
Derived secret{{671}}; // (Added) Uses Bob namespace in derived object
struct Container { int n= 888; Bob::Bar m{999}; }; // (Added) Uses Bob namespace
Container content; // (Added) Uses Bob namespace in container object
public:
// This constructor added to demonstrate indirect template references
Frequency( const Function& _function ) : function(_function)
{ using namespace ::SSN::debugging; // Without this, Bob::debugf is used
::SSN::debugging:: // Disambiguate
debugf("Frequency::Frequency(Coder: %p,%zd)\n", &_function, sizeof(_function));
// Drive the function, mostly just to see that it's been coded correctly
// Note the existence of function(DeviceFrequency& E) in namespace Bob.
// This *looks* like it might call Bob::function, but it doesn't,
// likely because function is an object containing an operator(), a
// std::function container.
Coder coder{2468}; function(coder); // (??TRICKY?? Not ADL ambiguous)
Coder rodoc{8642}; _function(rodoc); // (??TRICKY?? Not ADL ambiguous)
Bob::function(coder); // Just to verify it's different
}
Frequency( void ) // Default constructor
{ using namespace ::SSN::debugging; // Why isn't this enough??
using ::SSN::debugging::debugf; // Or this??
// Answer: When ADL comes into play, using statements are ignored.
// Also, without the first using, Bob::debugf would be used.
// (Rationale unclear, but that's the way it is.)
if( ssn_debug )
#if MAKE_IT_WORK
// As explained by #1201ProgramAlarm, it's the 'this' parameter that
// brought Bob's namespace into play for ADL name resolution.
// (Rationale unclear, but that's the way it is.)
::SSN::debugging:: // How can a developer predict that this is needed??
#endif
debugf("Frequency(%p,%zd)::Frequency\n", this, sizeof(*this));
// All remaining lines in Frequency added for detailed testing ============
const void* const that= this; // Another work-around
debugf("Frequency(%p,%zd)::Frequency\n", that, sizeof(*this)); // (Works)
Coder coder{3.14}; // (This is a Bob::DeviceFrequency)
debugf("Coder.f: %f\n", coder.f); // (No ambiguity)
Bob::debugf("Coder: %f\n", coder); // *** AMBIGUOUS debugf ***
debugf("f: %f\n", f); // (No ambiguity)
debugf("thing: %f\n", thing); // (No ambiguity)
debugf("Bob::thing: %f\n", Bob::thing); // (No ambiguity)
debugf("bar.bar: %d\n", bar.bar); // (No ambiguity)
SSN::debugging::debugf("bar: %d\n", bar); // *** AMBIGUOUS debugf ***
Bob::debugf("this->bar: %d\n", this->bar); // *** AMBIGUOUS debugf ***
debugf("foo.foo: 0x%3x\n", foo.foo); // (No ambiguity)
debugf("this->foo: 0x%3x\n", this->foo); // (No ambiguity)
debugf("Bob::bar.bar: %d\n", Bob::bar.bar); // (No ambiguity)
Bob::debugf("Bob::bar: %d\n", Bob::bar); // *** AMBIGUOUS debugf ***
debugf("secret.bar: %d\n", secret.bar); // (No ambiguity)
Bob::debugf("secret: %d\n", secret); // *** AMBIGUOUS debugf ***
debugf("content: %d\n", content); // (No ambiguity)
Bob::debugf("content.m: %d\n", content.m); // *** AMBIGUOUS debugf ***
// End of added lines =====================================================
}
}; // class Frequency
template<typename Coder>
struct Ugly_fix { // Macros stop ADL lookup, remove usings
#define debugf ::SSN::debugging::debugf
#define ssn_debug ::SSN::debugging::ssn_debug
Ugly_fix( void ) // Default constructor
{ if( ssn_debug ) debugf("Ugly_fix(%p)::Ugly_fix\n", this);
debugf("Bob::bar: %d\n", Bob::bar);
// Bob::debugf("Bob::bar: %d\n", Bob::bar); // Syntax error
}
#undef debugf // Macros (MUST BE) private
#undef ssn_debug
}; // struct Ugly_fix
class Non_template { public:
Non_template( void )
{ using namespace ::SSN::debugging;
if( ssn_debug )
debugf("Non_template(%p,%zd)::Non_template\n", this, sizeof(*this));
}
}; // class Non_template
} // namespace SSN::signals
// Namespace: **NONE**
static struct Handler { // Note: This actually *USES* ADL to find debugf
void operator()(Bob::DeviceFrequency& E) { debugf("E: %f\n", E); }
} handler; // struct Handler
struct Controller {
SSN::signals::Frequency<Bob::DeviceFrequency> bobcon;
SSN::signals::Frequency<Bob::DeviceFrequency> robcon;
SSN::signals::Ugly_fix<Bob::DeviceFrequency> ugly;
SSN::signals::Non_template non_template;
// Note that both Frequency constructors are used.
Controller( void ) : bobcon(), robcon(handler), ugly(), non_template() {}
}; // class Controller
// IMPLEMENTATION ============================================================
namespace SSN {
int debugging::ssn_debug= true;
namespace debugging {
void debugf(const char* fmt, ...) {
printf("SSN: "); // (For output disambiguation)
va_list argptr; va_start(argptr, fmt); vprintf(fmt, argptr); va_end(argptr);
} // debugf
} // namespace debugging
} // namespace SSN
namespace Bob {
double thing= 1122.33; // (Added for answer testing)
Foo bar{732}; // (Added for answer testing)
void debugf(const char* fmt, ...) {
printf("Bob: "); // (For output disambiguation)
va_list argptr; va_start(argptr, fmt); vprintf(fmt, argptr); va_end(argptr);
} // debugf
} // namespace Bob
// TEST ======================================================================
static inline int // Number of errors encountered
test_namespace_glitch( void ) // Test namespace glitch
{
int errorCount= 0; // Number of errors encountered
printf("\ntest_namespace_glitch\n");
Controller controller; // (Actually drive the constructor)
if( true ) {
errorCount++; // *Why* does 'this' import namespace?
printf("Still (partially) unexplained\n");
}
return errorCount;
}
(For those who like to run code, most of the test wrappers are also included here. You only need a main program that invokes the test.)
Here are the things I know I need to look out for:
Usage of this as a (bare) parameter inside of template code. (Thanks to #1201ProgramAlarm for pointing this "feature" out.) It's easy to do this accidentally, but hard to figure out why it happened.
Reference of an (uncontrolled) namespace named object (i.e. a struct or a class) used as a parameter to a function whether or not it's inside of template code. This is harder to do accidentally.
This reference can be indirect, as in the Frequency(const Function&) constructor, and therefore more prone to accidental use.
This may or may not be a complete list of ADL "gotcha's." There's a lot of examples in https://en.cppreference.com/w/cpp/language/adl that make me dizzy reading them, but the working ones don't usually seem to be things you might do accidentally once you've been bitten by ADL.
In my opinion, ADL shouldn't occur without explicit intent. Some language keyword or signal like [[adl]]std::swap(obj1,obj2) should be needed to activate it. But that's just me, I guess.
I don't know or understand the rationales behind the ADL design decisions to ignore using statements and to consider a bare this parameter a reference to a template object. I'm sure (OK, hoping) they exist. You may be interested in Andrew Koenig's short commentary A Personal Note About Argument-Dependent Lookup, found while diving into the mysteries of ADL in cppreference.com above.
Also, for the record, I'm up-voting and accepting #1201ProgramAlarm's answer because he explained that the this parameter brought ADL into play. When looking explicitly for it, I didn't find any documentation saying this would happen. While it doesn't answer my question, "How do I prevent this from happening?" I couldn't have gotten started without it.
What does help is that you get a seemingly unexplainable "ambiguous reference" error message that let's you know something strange and bad happened. Too late and too bad for whoever has to fix the problem. Too bad for someone who doesn't think about ADL, and might have to spend an inordinate amount of time first trying to isolate the problem and even then finally have to call on stackoverflow for help because ADL's just so weird.
I have an ugly way to bypass ADL, shown in the Ugly_fix constructor. By using (ugh) macros to auto-magically fully qualify names, not only is ADL prevented but the using statements are no longer needed. This kind of macro must be temporary, undefined after use, to avoid macro namespace pollution.

Related

Resolve std::string to something else than ::std::string - is it possible?

This is a follow-up question from: constructing string from NULL?
The following:
void test(const std::string& s);
int main(){
test(NULL);
}
Fails when run, but is legal c++.
In order to try to catch some of those cases, as an alternative ive considering if std::string can be replaced in the following way:
#include <string>
namespace {
namespace std {
struct string : public ::std::string { //so far everything is good
};
}
}
int main ()
{
std::string hello;//failure: ambiguous symbol
return 0;
}
Gives the following error:
<source>(17): error C2872: 'std': ambiguous symbol
C:/data/msvc/14.22.27905/include\string(19): note: could be 'std'
<source>(7): note: or '`anonymous-namespace'::std'
<source>(17): error C2872: 'std': ambiguous symbol
C:/data/msvc/14.22.27905/include\string(19): note: could be 'std'
<source>(7): note: or '`anonymous-namespace'::std'
Compiler returned: 2
I guess it is not possible to make it resolve without writing a more (fully) qualified name ? but is it possible to write std::string in the global namespace and have it resolve to something else while ::std::string is a valid type.
Background: after some failed attempts with cppcheck and cpp core check im trying to find all cases of std::string str = 0 or NULL or nullptr - since those will fail at runtime. And I thought this might be a way forward.
I ended up modifying the basic_string template ala. basic_string(int) = delete;
basic_string(::std::nullptr_t) = delete; - this won't catch all cases but does indeed seem to catch the direct cases at least
Resolve std::string to something else than ::std::string - is it possible?
[...]
...as an alternative ive considering if std::string can be replaced in the following way...
As far as I know, it is not possible outside of your anonymous namespace scope because you have no way to resolve the ambiguity (not to my knowledge).
As you can see below, since you are inside the scope of the anonymous namespace, it will be fine:
#include <string>
namespace
{
namespace std
{
struct string : public ::std::string
{
};
}
std::string hello; // Fine
}
int main()
{
std::string hello2; // Cannot be something else that ambiguous
return 0;
}
But even worse, the problem is not about std::string itself but about the std namespace.
Indeed, outside of the scope of your anonymous namespace, every call of std becomes ambiguous too.This is the error pointed by the compiler. There are two std namespaces accessible as is from the global scope.
So the following example becomes broken:
#include <string>
#include <vector>
namespace
{
namespace std
{
struct string : public ::std::string
{
};
}
std::string hello; // Fine
}
int main()
{
std::vector<int> a; // FAIL: reference to 'std' is ambiguous
return 0;
}
To fix this ambiguity over accessing the original std namespace, you'll need to write it as follows:
::std::vector<int> a; // Fully qualified name: Only way to refer to the `::std` namespace
As you can see, it still does not solve the issue and even worse, it adds a huge inconvenience.
Therefore the morality is:
Do not hide an already existing type but create a distinct one instead.
In the same way, do not hide a namespace (by defining the same namespace into an anonymous one --> evil).
(I'm open to any improvement proposal of this answer)
Could you use this kind of solution : Rather than working on std::string, you work on the function that will cause the problem
#include <string>
void test(const std::string& s){
}
// add a new function which use a pointer :
void test (const char* _Nonnull s) {
test(std::string(s));
}
int main()
{
test(NULL);
return 0;
}
Then clang will generate a warning :
1 warning generated.
ASM generation compiler returned: 0
<source>:13:14: warning: null passed to a callee that requires a non-null argument [-Wnonnull]
test(NULL);
see https://godbolt.org/z/PujFor

Debug function that "disappears" in some cases

Suppose I have a debug function that is defined like this:
namespace debug {
void report(std::string message);
}
Can I pull some compiler trick that will, when compiled, replace every call safely with a nop. I dont want to call an empty function, I want to not call the function at all.
If it is possible... can I make a namespace "disappear", too?
Debug executables will be compiled with the symbol DEBUGEXECUTABLE defined (I can imagine some tricks with macros).
You can do something like this:
namespace debug
{
void report(std::string message); // ToDo - define this somewhere
}
namespace release
{
template <class Y>
void report(Y&&){} // Intentionally do nothing
}
#if defined(DEBUGEXECUTABLE)
namespace foo = debug; // set foo to the debug namespace
#else
namespace foo = release; // set foo to the release namespace
#endif
Then use foo::report in your code. I like this since it minimises the use of preprocessor macros and keeps any compiler errors broadly similar across the debug and release configurations.
Passing a r-value reference in the release mode will allow the compiler to optimise out any anonymous temporaries. For the debug family of functions, you ought to pass strings by constant reference though to avoid any possiblity of a value copy being taken: void report(const std::string& message);
This is as optimal as I can make it.
We define DEBUG to have a report that does something, and leave it out it to do nothing: (or we can use whatever symbol you are using in your build process to distinguish debug and opt from production code)
#define DEBUG
We create two namespaces. One is called debug, the other release. In each we create an anonymous namespace, which makes it easy for the compiler to detect and discard unused functions:
namespace debug {
namespace {
void report(std::string const& s) {
std::cerr << s << "\n"; // sample implementation
}
}
}
namespace release {
namespace {
template<class T>
void report(T&&) {} // Or `class...Ts` and `Ts&&...` to handle more than 1 argument optionally.
}
}
Here we create a namespace alias that differs in release and debug:
#ifdef DEBUG
namespace report=debug;
#else
namespace report=release;
#endif
And our main:
int main() {
report::report("hello");
}
We can see the results of this under gcc 4.9 with DEBUG defined and not over at godbot. As you can hopefully see, when #define DEBUG is not defined, the compiler produces nothing but an empty main.
If it is defined, it compiles to what you'd expect.
namespace debug {
#ifdef DEBUGEXECUTABLE
void report(std::string message);
#else
inline void report(std::string message)
{
//nop - compiler should optimize it
}
#endif
}

C++: Defining void* array in header file and declaring it in a cpp file?

I saw this question and I tried to do as the answer to that question said. To use the extern keyword in the header file to define an array and then declare it outside of that namespace or class in a other cpp file.
It didn't work for me really, I'm not sure if it because I'm using a void pointer array (i.e void* array[]) or if it's just my ignorance that prevents me from seeing the problem.
This is the shortest example I can come up with:
[cpp.cpp]
#include "h.h"
void main(){
void* a::b[] = {
a::c = a::d(1)
};
}
[h.h]
namespace a{
struct T* c;
struct T* d(int e);
extern void* b[];
}
So the problem is that I receive the error:
IntelliSense: variable "a::b" cannot be defined in the current scope
And I have no clue why that is.
First, you should declare main() as int ! See here why.
Declaring your array as extern in a namespace means that it belongs to the namespace but is defined somewhere ele, normally in a separate compilation unit.
Unfortunately, in your main(), you try to redefine the element as a local variable. This explains the error message you receive.
You shoud do as follows:
#include "h.h"
void* a::b[] { a::c, a::d(1) }; // global variable belonging to namespace
int main() // int!!!
{
/* your code here */
}
The code will compile. The fact that a::b[] is defined in the same compiling unit is accepted. But the linker will complain because a::d(1) is a call to the function d returning a pointer to a struct, and this function is defined nowhere.
Therfore you should also define this:
namespace a {
struct T* d(int e)
{
return nullptr; // in reality you should return a pointer to struct T
}
}
Interestingly, struct T does not need to work for this code to compile and link.

Use case of C++11 function declaration at block scope?

It would seem I can declare a function at block scope:
int main()
{
void f(); // OK
}
However I can't define it:
int main()
{
void f() {}; // ERROR
}
My question is, of what use is a function declaration at block scope? What is a use case?
It's sometimes a shortcut to declaring and calling an externally-linked function which itself isn't publically defined in a header. For example, imagine you were linking against a C library which you knew provided a LibDebugOn(int) call but hadn't defined it in a header. It can be a shortcut to declare and call it in one place:
void myFunc() {
// call "Lib" but turn on debugging via hidden API
extern "C" void LibDebugOn(int); // declare hidden C-linked function
LibDebugOn(1); // call it
// do something with the library here...
LibDebugOn(0); // turn off lib debugging now
}
In fairness this is usually only worthwhile for a one-off quick hack, and not something to be encouraged.
You can define it. http://ideone.com/kJHGoF
#include <cstdio>
int main()
{
void f(); // Forward declare function named "f" returning void in the
// global namespace.
f();
}
/*
void g()
{
f(); // ERROR!
}
*/
void f()
{
std::puts("hello!");
}
I'm not sure why someone would actually want to use this. It is in the language this way for backwards compatibility with C; but I've got no idea what someone would do with this in C.

C++ compiler unable to find function (namespace related)

I'm working in Visual Studio 2008 on a C++ programming assignment. We were supplied with files that define the following namespace hierarchy (the names are just for the sake of this post, I know "namespace XYZ-NAMESPACE" is redundant):
(MAIN-NAMESPACE){
a bunch of functions/classes I need to implement...
(EXCEPTIONS-NAMESPACE){
a bunch of exceptions
}
(POINTER-COLLECTIONS-NAMESPACE){
Set and LinkedList classes, plus iterators
}
}
The MAIN-NAMESPACE contents are split between a bunch of files, and for some reason which I don't understand the operator<< for both Set and LinkedList is entirely outside of the MAIN-NAMESPACE (but within Set and LinkedList's header file).
Here's the Set version:
template<typename T>
std::ostream& operator<<(std::ostream& os,
const MAIN-NAMESPACE::POINTER-COLLECTIONS-NAMESPACE::Set<T>& set)
Now here's the problem: I have the following data structure:
Set A
Set B
Set C
double num
It's defined to be in a class within MAIN-NAMESPACE. When I create an instance of the class, and try to print one of the sets, it tells me that:
error C2679: binary '<<' : no operator found which takes a right-hand operand of type 'const MAIN-NAMESPACE::POINTER-COLLECTIONS-NAMESPACE::Set' (or there is no acceptable conversion)
However, if I just write a main() function, and create Set A, fill it up, and use the operator- it works.
Any idea what is the problem? (note: I tried any combination of using and include I could think of).
Strange - even though putting free functions associated with a type to a different namespace is a bad practice, the global namespace declarations are always visible.
The only thing I can think of is that declaration with the same name in MAIN-NAMESPACE would shadow the one in the global namespace - isn't there an operator<<, possibly for totally unrelated type, in MAIN-NAMESPACE? If so, you should fix that by using ::operator<< declaration in MAIN-NAMESPACE. Example:
namespace A
{
namespace B
{
class C{};
}
}
void f(A::B::C*);
namespace A
{
void f(int*); // try commenting
using ::f; // these two lines
void g()
{
B::C* c;
f(c);
}
}
OK I figured this out.
jpalecek's intuition about there existing another operator<< in the namespace was correct (apparently I forgot to comment it out).
The lookup rules for namespaces first start the search in the function call's namespace and search up the enclosing namespaces, right up to the global namespace (then it does the Argument dependent lookup if no match is found). However, if along the way it finds some match for operator<<, it stops the search, regardless of the fact that the types used in those functions may be incompatible, as was the case here.
The solution is either to include it into the MAIN-NAMESPACE (which I'm not allowed to), or import it from the global namespace with "using ::operator<<".
Try calling the function explicitly?
::operator<<( cout, myObj );
As SoaBox pointed out, try calling it explicitly.
For your information, if you wish to call a global function which has been hidden in the current namespace precede the function with :: to bypass the local function and call the global function.
Try calling the function explicitly?
::operator<<( cout, myObj );
Yes, that does work!
it will try to find the f function in
the current namespace (at the place of
call) or in the enclosing namespaces
of c1 and c2 types (namespace1,
namespace2::namespace3), but it will
not try other namespaces in the
search.
So let's see if I got this right: the reason invoking the operator<< from a main() function worked is because I was in the global namespace (as was operator<<).
The reason it failed when invoking from the class I implemented is because the class was in a not global namespace and there were no variables in it that pointed the compiler towards the global namespace.
OK people asked for a specific examples, so here's the relevant part of the code.
//Disclamer: in the slim case someone from my uni sees this, encounters it in the submission file, and decides I copied it or something, my student number is 311670137
This is the header file Set.h:
namespace MTM {//This is the MAIN-NAMESPACE
namespace PointerCollections {
(ITERATORS AND PREDICATE CLASSES)
template<typename T>
class Set {
public:
/////////////////////////////////
// Definitions
/////////////////////////////////
private:
/////////////////////////////////
// Definitions
/////////////////////////////////
};
///////////////////////////////////////////////////////////////////////////////
// The implementation part.
///////////////////////////////////////////////////////////////////////////////
}
}
// operator<< - the same a Set::print(std::ostream& os,
// const BinaryPredicate<T>& predicate)
// function called with the 'predicate' parameter omitted
template<typename T>
std::ostream& operator<<(std::ostream& os,
const MTM::PointerCollections::Set<T>& set){
set.print(os);
return os;
}
This is what I defined in a different file:
namespace MTM {
using std::ostream;
class Schedule {
public:
///////////////////
//Definitions, including:
///////////////////
void registerStation(string stationName);
void reportRegisteredStations(std::ostream& outputStream) const;
private: //My database
//All the classes Set recieves are defined elsewhere
Set<RegisteredStation> places;
Set<BusLine> busses;
Set<TrainLine> trains;
double tarifForBuses;
double tarifForTrains;
};
}
And here's from the main:
Schedule s();
s.registerStation("1");
s.reportRegisteredStations(cout);//This invokes the error. Definition follows:
reportRegisteredStations is defined as:
void Schedule::reportRegisteredStations(std::ostream& outputStream) const{
outputStream<<places;
}
This works for me
#include <iostream>
#include <string>
using std::string;
namespace MTM {//This is the MAIN-NAMESPACE
namespace PointerCollections {
template<typename T>
class Set {
};
}
}
template<typename T>
std::ostream& operator<<(std::ostream& os,
const MTM::PointerCollections::Set<T>& set){
return os;
}
namespace MTM {
using std::ostream;
using PointerCollections::Set;
class Schedule {
public:
///////////////////
//Definitions, including:
///////////////////
void registerStation(string stationName);
void reportRegisteredStations(std::ostream& outputStream) const;
private: //My database
//All the classes Set recieves are defined elsewhere
Set<int> places;
Set<int> busses;
Set<int> trains;
double tarifForBuses;
double tarifForTrains;
};
void Schedule::reportRegisteredStations(std::ostream& outputStream) const{
outputStream<<places;
}
}
int main()
{
MTM::Schedule s;
s.reportRegisteredStations(std::cout);
}
CORRECTION: The text below is based on experience with the g++ family of compilers. After the comment to the answer I have reread the standard (which states that ADL will be used over regular name lookup, and regular name lookup should find the operator<<). I have also tried with comeau compiler (the most standard compliant compiler I know of) and the symbol is found. It seems as a problem with g++ (tried versions 3.3, 4.1, 4.3).
Original answer:
Search for Koening lookup (technically ADL: Argument dependent lookup).
The short answer is that if you have the following class:
namespace test {
class A {};
}
the stream insertion operator should be defined as:
namespace test {
std::ostream& operator<<( std::ostream&, A const & );
}
Functions or operators should be defined in the same namespace as one of the arguments that it takes. (*)
When the compiler finds a function call such as:
namespace test2 {
void g() {
namespace1::class1 c1;
namespace2::namespace3::class2 c2;
f( c1, c2 );
}
}
it will try to find the f function in the current namespace (at the place of call) or in the enclosing namespaces of c1 and c2 types (namespace1, namespace2::namespace3), but it will not try other namespaces in the search.
(*) In this case, you are pretty much limited to the test namespace, as you are not allowed to add a function to the std namespace (only template specializations).
End of original post.
Even if as commented before this may just be a problem with the compiler, it is common usage and recommended to define all free functions that operate on a user defined type in the same namespace as the type itself.