4

I'm trying to develop a nested builder class in C++, but I keep getting this "invalid use of incomplete type 'class Npc::Builder<'T>" error. I've searched a lot and I couldn't find an answer. Can anyone help me, please?

Here's my code:

Npc.h
class Npc{
     ...
    template<class T, class enable_if<is_base_of<Npc, T>::value>::type* = nullptr>
    class Builder{

    private:
        T* instance;

    public:
        Npc::Builder<T>* create();
        Npc::Builder<T>* name(string name);
        Npc::Builder<T>* charClass(string charClass);
        Npc::Builder<T>* hp(int hp);
        Npc::Builder<T>* mana(int mana);
        Npc::Builder<T>* attackPower(int attackPower);
        Npc::Builder<T>* magicPower(int magicPower);
        Npc::Builder<T>* defense(int defense);
        Npc::Builder<T>* magicDefense(int magicDefense);
        T* build();
    };
};


Npc.cpp
...
template<class T, class enable_if<is_base_of<Npc, T>::value>::type* = nullptr>
Npc::Builder<T>* Npc::Builder<T>::create() {
   ...
}

main.cpp
...
Npc::Builder<Warrior>* builder = new Npc::Builder<Warrior>();
...

Thanks!

lcleite17
  • 43
  • 5
  • I don't think that's the real problem. Indeed one must implement the template in headers, however this only leads to linker errors. Here OP has compile-time errors, which most likely come arise from the lack of `typename`. – vsoftco Jun 25 '16 at 00:34
  • 1
    Move your npc implementation to .h file. All templated code must be accessible from source where it is used. For example if you have a templated class - it must be a header only class if you need to use it in outer code – fnc12 Jun 25 '16 at 04:59

1 Answers1

2

There are some syntactical errors in your code. Most importantly, you are missing a typename (as you have a dependent type), and also the second template parameter is a non-type in this case, as you are defaulting it to a nullptr. Use

// non-type param
template<class T, typename std::enable_if<std::is_base_of<Npc, T>::value>::type* = nullptr> 
class Builder { ... };

or, making it a type

// type param
template<class T, class = typename std::enable_if<std::is_base_of<Npc, T>::value>::type> 
class Builder { ... };

Next, the line

template<class T, enable_if<is_base_of<Npc, T>::value>::type* = nullptr>
Npc::Builder<T>* Npc::Builder<T>::create() {
   ...
}

is incorrect: missing typename, cannot have default param. in definition, and also the compiler believes that you are partially-specializing the template member function, which is not allowed. What you need is something like:

template<class T, typename std::enable_if<std::is_base_of<Npc, T>::value>::type* U>
Npc::Builder<T, U>* 
Npc::Builder<T, U>::create() {
    return nullptr;
}

The code below shows a complete working example that fixes all of the issues mentioned above:

#include <iostream>
#include <type_traits>

class Npc {
public: 
    template<class T, 
             typename std::enable_if<std::is_base_of<Npc, T>::value>::type* U = nullptr>
    class Builder {
    private:
        T* instance;    
    public:
        Builder* create(); // no need for Noc::Builder<T>, due to template name injection
        Builder* name(std::string name);
        Builder* charClass(std::string charClass);
        Builder* hp(int hp);
        Builder* mana(int mana);
        Builder* attackPower(int attackPower);
        Builder* magicPower(int magicPower);
        Builder* defense(int defense);
        Builder* magicDefense(int magicDefense);
        T* build();
    };
};

struct Warrior: Npc{};

template<class T, typename std::enable_if<std::is_base_of<Npc, T>::value>::type* U>
Npc::Builder<T, U>* 
Npc::Builder<T, U>::create() {
    return nullptr;
}

int main()
{
    Npc::Builder<Warrior>* builder = new Npc::Builder<Warrior>();
    delete builder;
}

Live on Coliru

Community
  • 1
  • 1
vsoftco
  • 55,410
  • 12
  • 139
  • 252
  • I'm still getting the error when I add the definition of create() method. Like [this](http://coliru.stacked-crooked.com/a/efa4a0463fc7475b) – lcleite17 Jun 25 '16 at 02:30
  • @lcleite17 See the edit, I figured out what was happening. The solution I posted should work, it compiles for me with gcc/clang. – vsoftco Jun 25 '16 at 03:04