1

Trying to compile the code below fails due to a "conflicting declaration". Why can't I define a forward declared class like this?

I did this to hide that the implementation uses a certain library. Although I'll admit that this does not really abstract anything - you'll still need to know what the implementation uses to make the correct call - I am still interested in why this doesn't work.

Bar.cpp:

#include "Bar.hpp"
#include "Foo.hpp"

using Foo = ns::Foo;

void Bar::foo(Foo f) {
}

Bar.hpp:

class Foo;

class Bar {
    void foo(Foo f);
};

Foo.hpp:

namespace ns {

    class Foo {
    };
}

To be clear, I want to know why can't define a previously declared class by aliasing - in other words saying "use that definition over there that has a different name"

Oebele
  • 581
  • 1
  • 4
  • 17

2 Answers2

4

You are declaring Foo twice with conflicting types. First, you declare Foo in Bar.hpp as:

class Foo;

Subsequently, you declare foo in Bar.cpp as:

using Foo = ns::Foo;

You cannot put a forward declaration like that if you define in your source file an alias with the same name, because then you declare two different types with the exactly the same name.

Based on your question I assume that you want to use Foo, without its namespace in the Bar.cpp. The solution is as following:

Bar.cpp

#include "Bar.hpp"
#include "Foo.hpp"

using ns::Foo;

void Bar::foo(Foo f) {}

Bar.hpp

namespace ns
{
    class Foo;
}

class Bar 
{
    void foo(ns::Foo f);
};

Foo.hpp

namespace ns
{
    class Foo
    {};
}
Chiel
  • 6,006
  • 2
  • 32
  • 57
  • Why are the types conflicting? Both are classes, and ::Foo has not been defined before defining it with the `using` statement. Please note that I want to know why it doesn't work more than being interested in a fix. – Oebele Mar 17 '16 at 10:44
  • 1
    First you are creating a typedef that says that `Foo` is equal to `ns::Foo`. Later you are redeclaring `Foo` as a class in the global namespace. So what is `Foo` then? Just `Foo`, or `ns::Foo`?. I don't know, and neither does the compiler. – Chiel Mar 17 '16 at 10:51
  • Ah, I did not know that it counted as a typedef. I have a similar case now, where a library provides a concrete version of a template class via a `using` statement. How would I forward declare that? – Oebele Mar 17 '16 at 11:08
  • Forward declaring is not a problem as long as you don't omit the namespace, because they become different declarations made with the same name, which leads to conflicts. – Chiel Mar 17 '16 at 11:09
  • 1
    Your answer reads a little backwards - you're saying Bar.cpp happened before Bar.h. – Barry Mar 17 '16 at 11:33
0

Because in order for the Bar::Foo to work, the Bar class needs to know the size of the Foo argument as early as declaring it. You could make this work, by declaring void foo(Foo* f); That way, instead of knowing the size of Foo, the compiler will only care for the size of a pointer. Simply use to pick the namespace you need:

using namespace ns;
Dimo Markov
  • 422
  • 2
  • 9
  • 1
    But that does not solve the namespace issue that Oebele mentions. – Chiel Mar 17 '16 at 10:15
  • Why not simply using: `using namespace ns;` instead of `using Foo = ns::Foo;` – Dimo Markov Mar 17 '16 at 10:21
  • @DimoMarkov I might not want to pull in the entire namespace, for example due to naming conflicts. Especially when this Foo class is the only one I use a lot from a large namespace. – Oebele Mar 17 '16 at 10:42
  • Then you can do what I suggest: `using ns::Foo;`. This only pulls in Foo, and not the rest of namespace `ns`. – Chiel Mar 17 '16 at 10:52
  • As @Chiel mentioned in his answer, you should forward declare the Foo in the namespace you would want to use it. The Bar class requires information about Foo type in order to declare the member function. Without providing this information in the header, your implementation won't be in sync, and therefore you would have a conflict. Unless you declare Bar::foo with argument Foo*, as I mentioned in my answer. – Dimo Markov Mar 17 '16 at 10:52
  • @Chiel of course, I just wanted to point out the flaw in pulling in an entire namespace as a response to Dimo's "Why not simply using: `using namespace ns;` instead of `using Foo = ns::Foo;`" – Oebele Mar 17 '16 at 11:11
  • @Oebele. This is not the same. The first makes the functions in the namespace available in scope of the statement, whereas the second is a typedef of a new type with that specific name, which is not equal to writing `using ns::Foo`; – Chiel Mar 17 '16 at 11:13