8

I created a class and I want to force anyone who's trying to construct an object, to use unique_ptr. To do that I thought of declaring the constructor protected and use a friend function that returns a unique_ptr. So here's an example of what I want to do:

template <typename T>
class A
{
public:
    friend std::unique_ptr<A<T>> CreateA<T>(int myarg);

protected:
    A(int myarg) {}
};

template <typename T>
std::unique_ptr<A<T>> CreateA(int myarg)
{
    // Since I declared CreateA as a friend I thought I
    // would be able to do that
    return std::make_unique<A<T>>(myarg);
}

I did some reading on friend functions and I understood that a friend function provides access to private/protected members of an object of a class.


Is there anyway I can make my example work?

Even without friend functions, my goal is to make the CreateA the only way for someone to create an object.

EDIT

I change the code a bit. I didn't mention that my class takes one template parameter. That makes things more complex apparently.

TheCrafter
  • 1,779
  • 1
  • 18
  • 39
  • 1
    related: http://stackoverflow.com/questions/8147027/how-do-i-call-stdmake-shared-on-a-class-with-only-protected-or-private-const – Brian Bi Nov 24 '15 at 19:22
  • What's the purpose of the friend method here? If your only goal is to keep others from using the constructor, simply making it private or protected (if you want to inherit this class later) is sufficient. – Chase Henslee Nov 24 '15 at 19:38
  • see also "named constructor" – 2785528 Nov 24 '15 at 19:43
  • In your question, `A>` always has a constructor taking `int` . Is this also true in the real code; or does the real code have constructor taking `T` (and perhaps more arguments) ? – M.M Nov 24 '15 at 22:15

3 Answers3

3

You can do it this way :-

#include <iostream>
#include <memory>
using namespace std;
class A
{
    int arg;
public:
    friend unique_ptr<A> CreateA(int myarg);
    void showarg() { cout<<arg; }

protected:
    A(int myarg): arg(myarg) {}
};
unique_ptr<A> CreateA (int myarg)
{
    return std::unique_ptr<A>(new A(myarg));
}
int main()
{
    int x=5;
    unique_ptr<A> u = CreateA(x);
    u->showarg();
    return 0;
}

Output :-

5

If you don't want to use friend function you can make the function static & call it like this :-

unique_ptr<A> u = A::CreateA(x);

EDIT :-

In reply to your edit I rewrote the program & it goes like this :-

#include <iostream>
#include <memory>
using namespace std;
template <typename T>
class A
{
    T arg;
public:
    static std::unique_ptr<A> CreateA(T myarg)
    {
        return std::unique_ptr<A>( new A(myarg) );
    }
    void showarg()
    {
        cout<<arg;
    }
protected:
    A(T myarg): arg(myarg) {}
};

int main()
{
    int x=5;
    auto u = A<int>::CreateA(x);
    u->showarg();
    return 0;
}

Simple & easy !!! But remember you cannot instantiate the object of A. Good Luck !!!

Ankit Acharya
  • 2,225
  • 1
  • 13
  • 26
2

Create a static function that instantiates the protected constructor.

  #include<iostream>
#include<string.h>
#include<ctype.h>
#include<math.h>
#include <memory>
using namespace std;
template< typename T >
class A
{
public:
    static void CreateA(int myarg, std::unique_ptr<A<T>>& objA, T t) {
        std::unique_ptr<A<T>> objB(new A(myarg, t));
        objA = std::move(objB);
    }

protected:
    A(int myarg, T t) {
        m_t = t;
    }

private:
    T m_t;
};

int main() {

    int myArg = 0;
    std::unique_ptr<A<int>> anotherObjA;
    A<int>::CreateA(myArg, anotherObjA, myArg);


    return 0;
}
Nandu
  • 788
  • 5
  • 10
2

The other answers suggest using a static template function, which I agree is the best solution, because it is simpler.

My answer explains why your friend approach didn't work and how to use the friend approach correctly.


There are two problems in your original code. One is that make_unique is not actually a friend of A, so the call make_unique<A<T>>(myarg); does not have access to A's protected constructor. To avoid this , you can use unique_ptr<A<T>>(new A(myarg)) instead. Theoretically it would be possible to declare make_unique a friend but I'm not even sure of the right syntax for that.

The other issue is the template friends problem. Inside a class template, friend <function-declaration> actually declares a non-template friend.

The C++ FAQ suggests two possible workarounds. One of them is to define the friend function inline. However, in that case the function can only be found by argument-dependent lookup. But since the function does not take A<T> (or A<T> &) as argument, it can never be found this way. So this option is not viable to your situation -- it's more suited to operator overloading.

So the only fix is to declare (and optionally define) the template function before the class definition:

#include <memory>

template<typename T>
class A;

template <typename T>
std::unique_ptr<A<T>> CreateA(int myarg)
{
    return std::unique_ptr<A<T>>{new A<T>(myarg)};
}

template <typename T>
class A
{
    friend std::unique_ptr<A<T>> CreateA <> (int myarg);
//          refers to existing template  ^^

protected:
    A(int myarg) {}
};

int main()
{
    auto x = CreateA<int>(5);
}

Note: It is possible to declare CreateA where I have defined it, and put the function definition later. However, the code I have posted works -- despite A not being defined when new A<T>(myarg) appears in the source -- because CreateA is not instantiated until it is called, at which point A will be defined.

M.M
  • 130,300
  • 18
  • 171
  • 314
  • why does the code throw error if we define the class first & then the `friend` function – Anwesha Nov 24 '15 at 22:59
  • you don't need to mention the type, `CreateA` deduces it without you mentioning in this case – Ankit Acharya Nov 24 '15 at 23:10
  • @AnkitAcharya no it doesn't, perhaps you are mentally mixing up this code with the code in your answer (which uses `T myarg` instead of `int myarg`) – M.M Nov 25 '15 at 00:43
  • @Anwesha see the paragraph of my answer starting "The other issue is..." , including link to C++ FAQ – M.M Nov 25 '15 at 00:44