Question

Warning, C++ hating ahead... I have seen here slightly different variations of this issue, here is my take

namespace space {
}

template <typename T> struct C
{
   void foo() {
      using namespace space;
      bar(t);
   }
   T t;
};

class A {
};

namespace space {

void bar(const A& a){
}

}

int main()
{
   C<A> c;
   c.foo();
   return 0;
}

if bar is not inside a namespace, everything compiles ok. Putting a namespace breaks compilation with gcc. What is more interesting - gcc finds the function, but just don't feel like using it:

ConsoleApplication1.cpp: In instantiation of 'void C<T>::foo() [with T = A]':
ConsoleApplication1.cpp:26:10:   required from here
ConsoleApplication1.cpp:8:8: error: 'bar' was not declared in this scope, and no declarations were found by argument-dependent lookup at the point
instantiation [-fpermissive]
   bar(t);
        ^
ConsoleApplication1.cpp:18:6: note: 'void space::bar(const A&)' declared here, later in the translation unit
 void bar(const A& a){

Is there any sane reason for this behaviour, except that the standard says so? And is there any switch I can make gcc accept this code, since it seems there is no technical issue, and I do not see why such code should not work from a pure language standpoint.

Was it helpful?

Solution 2

Is there any sane reason for this behaviour, except that the standard says so?

Sane or not, C++ usually requires names to be declared before use. You don't declare bar before using it; specifically, using namespace space only imports names that have been declared at that point, and not bar.

(If you care about the reason, it's because C++ inherited its declaration rules from the languages of half a century ago, when computers were somewhat primitive. Rules like this allowed compilation in a single pass, so that the compiler didn't have to keep waiting for the operator to put the stack of punch cards back into the hopper. Or something like that; I'm not quite sure how computers worked back then.)

And is there any switch I can make gcc accept this code

As the error message says, -fpermissive. But your code won't be portable if you don't stick to the standard.

OTHER TIPS

templates are not macros. Name lookup of symbols is done in two contexts: first, where you wrote the template, and second pass of only argument-dependent lookup (ADL, or Koeing Lookup) when you pass the template a type (when the template "factory" is instantiated into an actual class or function).

When the function is in the same namespace as A, it is found via ADL. When it is not, it will not be. So when you moved the bar function into space::bar, it was no longer ADL-found with an argument of type A.

This is intentional, to both prevent your template from meaning completely different things at different spots, and to allow non-member interface extensions to types in their own namespace only.

Either use a traits class, stick such helper functions in the same namespace as the type, or pass in functors.

A traits class is probably easiest. Create a class with a static function foo (if you want a default implementation: otherwise leave empty). Call it from your template. Specialize it for A. Now you can implement special behavior for your A type at that point -- it could even call your space::bar function if that function is in view.

Putting bar in the same namespace as A is another solution, and I find it scales better than traits classes. Many of my personal traits classes end up falling back on an ADL lookup, as that lets me inject the handling code right next to where I define A. (The use of traits classes lets me also have my trait handle things in std, where I am not allowed to inject functions for ADL purposes for types living in std (you can inject ADL functions into std, but only for your own types))

The functor solution is how std::map works -- while it falls back on std::less<T> which falls back on operator<, it takes a functor that lets you specify how the key type should be compared within this map.

This error has nothing to do with namespaces or templates at all, but is the standard error of using a function before it has been declared. Just as you cannot do:

void caller() { callee(); }
void callee() {}

But must instead do:

void callee() {}     
void caller() { callee(); }

Or:

void callee();     
void caller() { callee(); }
void callee() {}

... the same applies for more complicated functions involving templates and namespaces. Note that if you reorder slightly, it works just fine:

class A {
};

namespace space {
void bar(const A& a){
}
}

template <typename T> struct C
{
  void foo() {
    using namespace space;
    bar(t);
 }
 T t;
};


int main()
{
  C<A> c;
  c.foo();
  return 0;
 }
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top