How can I access a protected constructor from a friend function?
Asked Answered
C

3

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.

Carolyn answered 24/11, 2015 at 19:18 Comment(4)
related: #8147527Heartstrings
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.Hudgens
see also "named constructor"Songer
In your question, A<T>> 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) ?Indefinite
D
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 !!!

Dunson answered 24/11, 2015 at 20:27 Comment(6)
That seems to work but having a template parameter for class A makes things more complex. I updated my question to include that.Carolyn
It works with your edit but I noticed something really strange! If instead of returning a unique_ptr using the new I do this: return std::make_unique<A>(myarg) I get an error. Isn't that strange? I mean, I thought make_unique just calls new and returns a unique_ptr. I am confused.Carolyn
make_unique uses the return unique_ptr<T> (new T(...)) statementDunson
no because make_unique acts like it's instantiating object of the class & hence fails.Dunson
Ooh okay got it! Thank you.Carolyn
The reason make_unique<A>(myarg) fails is because make_unique is not a friend, so it cannot invoke the protected constructorIndefinite
L
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;
}
Loraineloralee answered 24/11, 2015 at 19:32 Comment(0)
I
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.

Indefinite answered 24/11, 2015 at 22:12 Comment(4)
why does the code throw error if we define the class first & then the friend functionNanette
you don't need to mention the type, CreateA deduces it without you mentioning in this caseDunson
@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)Indefinite
@Nanette see the paragraph of my answer starting "The other issue is..." , including link to C++ FAQIndefinite

© 2022 - 2024 — McMap. All rights reserved.