2

I am new to template function with variadic arguments. The code , inspired by a Codeview article , don't compile at the CreateBook(), need help.

I want to create an object using factory method and template .

using namespace std;

template<class T>
class bar
{
public:
    template<class... Args>
    static bar<T> CreateBook(Args&&... args)
    {
        return bar<T>(std::forward<Args>(args)...); // Don't compile here
    }

    T get()
    {
        return val;
    }
private:

    bar(const T& t) :val(t) {}
    T val;

};

struct book
{
    string name;
    int phone;
    book(string s, int t) : name(s), phone(t) {}
};

void print(bar<book> k)
{
    cout << k.get().name << " " << k.get().phone << endl;
}

int main()
{
    bar<book> b = bar<book>::CreateBook("Hello World",91520);

    print(b);

    return 0;
}
3
  • 1
    Can you add error message of compiler? Commented Apr 1, 2018 at 8:15
  • @R2RT: It says initialization error during return in createBook(). Commented Apr 1, 2018 at 15:14
  • Mohit and seccpur replies work as expected, but I am not sure which is the right way, yet. Commented Apr 1, 2018 at 15:18

3 Answers 3

1

See this code. Your bar constructor should handle pack argument too.

template<class... X>
bar(X... x) :val(x...) {}
Sign up to request clarification or add additional context in comments.

Comments

1

I tried to compile your code and I get errors something like no matching function for call to 'bar<book>::bar(const char [12], int)' therefore I added T(...) to the line marked as "Don't compile here." and it worked! Now the code looks something like this:

#include <iostream>
#include <string>
#include <algorithm>
#include <utility>

using namespace std;

template<class T>
class bar
{
public:
    template<class... Args>
    static bar<T> CreateBook(Args&&... args)
    {
        return bar<T>(T(std::forward<Args>(args)...)); // Fixed this line.
    }

    T get()
    {
        return val;
    }
private:

    bar(const T& t) :val(t) {}
    T val;

};

struct book
{
    string name;
    int phone;
    book(string s, int t) : name(s), phone(t) {}
};

void print(bar<book> k)
{
    cout << k.get().name << " " << k.get().phone << endl;
}

int main()
{
    bar<book> b = bar<book>::CreateBook("Hello World",91520);

    print(b);

    return 0;
}

2 Comments

Thanks, it works, but why a second T() is required in the return bar<>() constructor?
@ark1974 I think that the constructor of bar was being called with 2 arguments in the previous code but it has just one constructor that takes only 1 argument that is of type const T&. So a T(...) was required to pass just 1 argument to the constructor of bar<T> and the constructor of T with 2 arguments.
0

Every problem can be solved by adding one more layer.

First lets isolate the job of Factory. It should do only one task. Factory provides an interface on top on the underlying object of type T.

template <typename T>
class Factory
{
    public:
        // prefer copy and move, if you have C++11
        Factory(Type d) : data(std::move(d)) {} 

        // const getter
        T   get() const { return data; };

        // non-const reference getter, for modifications
        T & get()       { return data; };
    private:
        T data;
};

Now lets create a helper function on top of it, to help us manage and allocate objects. The job of this MakeObject is to make an object and hand over the object to a manager, i.e. Factory. It then returns the instance of that managed object.

template<typename Object, typename ... Args>
Factory<Object> MakeObject(Args && ... args)
{
    return Factory<Object>{ Object{ std::forward<Args>(args) ... } };
}

Now we can combine the two of them independently, to create something that is desired.

#include <iostream>

template <typename T>
class Factory
{
    public:
        Factory(Type d) : data(std::move(d)) {}
        T   get() const { return data; }; 
        T & get()       { return data; };
    private:
        T data;
};

template<typename Object, typename ... Args>
Factory<Object> MakeObject(Args && ... args)
{
    return Factory<Object>{ Object{ std::forward<Args>(args) ... } };
}

struct book
{
    std::string name;
    int         phone;
    book(std::string s, int t) : name(s), phone(t) {}
};

void print(Factory<book> const & k)
{
    std::cout << "[ Factory ] : " << k.get().name << ", " << k.get().phone << std::endl;
}

int main()
{
    auto obj = MakeObject<book>("Hello World", 91520);
    print(obj);
    return 0;
}

I didn't use the static function CreateBook because the intent is not clear. If you really want to use static, now you have the decoupled code, and it can be implemented and tested independently in the class Factory.

1 Comment

It seems, this is a complete implementation of a factory pattern. I want to learn how to use variadic template functions to accept different objects in the factory method. Thanks for your effort.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.