简体   繁体   中英

How can I access a protected constructor from a friend function?

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.

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 !!!

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;
}

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM