0

My issue extends this one: How to declare a variadic template function as a friend?

I know how to make a template variadic function a friend of a class for all instantiations of template arguments. But I did not figured out how to make friends from only some explicitly listed instantiations.

Example:

template <typename T, typename... Args>
T create(Args&&... args)
{
    T t(forward<Args>(args)...);
    // ...
    return t;
}

class A {
public:
    /* -- This would allow to use all versions of constructors ..
    template <typename T, typename... Args>
    friend T create(Args&&... args);
    */
    // How to make these work?
    friend A create<A>(int);
    friend A create<A>(int, int);
    // .. is replacing T by A necessary at all?
protected:
    A() = default;  // < this should 'stay protected'!
    A(int) { }
    A(int, int) { }
};

The goal is to make friends from only some versions of create, which means that I want only some versions of constructors to be accessible in public, but others not.

I also want friend creates to stick only to A return types (and constructors), but maybe it is not necessary at all since friends are not inheriting and other constructors than A may not be influenced inside A?

The solution can require features from up to C++14, but preferably not from newer standards.

EDIT:

If I needed only elementary types like int then @ritesh solution would be completely satisfactory as copying elements is equally efficient as moving. However, my use case is actually different and needs to avoid unnecessary copying of more complex objects, and constructors can have arguments that are passed by value, either as a copy or moved into (I can't remember how this idiom is called .. see the example below).

I've done this solution, which uses (and checks) forward references as @Jarod42 came up with. I used a helper struct E to demonstrate copying and moving its objects.

template <typename T, typename... Args>
T create(Args&&... args)
/** ++ This would have copy E even if it is not necessary ..
T create(Args... args)
*/
{
    T t(forward<Args>(args)...);
    // ...
    return t;
}

struct E {
    E() { cout << "E()" << endl; }
    E(const E&) { cout << "E(const E&)" << endl; }
    E& operator =(const E&) { cout << "= const E&" << endl; return *this; }
    E(E&&) { cout << "E(E&&)" << endl; }
    E& operator =(E&&) { cout << "= E&&" << endl; return *this; }
};

class A {
public:
    /** -- This would allow to use all versions of constructors ..
    template <typename T, typename... Args>
    friend T create(Args&&... args);
    */

    friend A create<A>(const E&);
    friend A create<A>(E&);
    friend A create<A>(E&&);
    friend A create<A>(const E&, const E&);
    friend A create<A>(const E&, E&);
    friend A create<A>(const E&, E&&);
    friend A create<A>(E&, const E&);
    friend A create<A>(E&, E&);
    friend A create<A>(E&, E&&);
    friend A create<A>(E&&, const E&);
    friend A create<A>(E&&, E&);
    friend A create<A>(E&&, E&&);

    /** ++ This would have copy E even if it is not necessary ..
    friend A create<A>(E);
    friend A create<A>(E, E);
    */
protected:
    A() = default;  //< this should 'stay protected'!
    A(E) { }  //< either copy or move E
    A(const E&, E) { }  //< must not copy the first argument
};

Luckily, it is sufficient to declare as friends only variants with reference argument types (const E&, E&, E&&), but not E: this is what forward will take care of.

Test:

E e;
const E ce;

cout << endl << "(E) <- (e) ..." << endl;
create<A>(e);
cout << endl << "(E) <- (ce) ..." << endl;
create<A>(ce);
cout << endl << "(E) <- (E()) ..." << endl;
create<A>(E());

cout << endl << "(const E&, E) <- (e, e) ..." << endl;
create<A>(e, e);
cout << endl << "(const E&, E) <- (ce, ce) ..." << endl;
create<A>(ce, ce);
cout << endl << "(const E&, E) <- (ce, e) ..." << endl;
create<A>(ce, e);
cout << endl << "(const E&, E) <- (ce, E()) ..." << endl;
create<A>(ce, E());
cout << endl << "(const E&, E) <- (move(e), E()) ..." << endl;
create<A>(move(e), E());

.. will produce:

E()
E()

(E) <- (e) ...
E(const E&)

(E) <- (ce) ...
E(const E&)

(E) <- (E()) ...
E()
E(E&&)

(const E&, E) <- (e, e) ...
E(const E&)

(const E&, E) <- (ce, ce) ...
E(const E&)

(const E&, E) <- (ce, e) ...
E(const E&)

(const E&, E) <- (ce, E()) ...
E()
E(E&&)

(const E&, E) <- (move(e), E()) ...
E()
E(E&&)

I believe now that this solution works, it's only a little uncomfortable since it is necessary to declare all combinations of references (including E&, otherwise passing e will not work). If anyone has an idea how to shorten this (maybe by utilizing templates, but not macros, I seek to avoid using them), please let me know.

2 Answers2

4

According to your signature, it should be something like:

friend A SOS::create<A>(int&&);
friend A SOS::create<A>(int&&, int&&);

friend A SOS::create<A>(int&);
friend A SOS::create<A>(int&, int&);

friend A SOS::create<A>(const int&);
friend A SOS::create<A>(const int&, const int&);

// ... mixing int&&, const int&    handles volatile

Demo

Jarod42
  • 203,559
  • 14
  • 181
  • 302
1

This code will work.

template <typename T, typename... Args>
T create(Args... args)
{
    return T(std::forward<Args>(args)...);

}

class A {
public:
    /*This will work*/
    friend A create<A>(int);
    friend A create<A>(int, int);
    void foo() { std::cout << "in foo" << std::endl; };
    // .. is replacing T by A necessary at all? 

protected:
    A() = default;  // < this should 'stay protected'!
    A(int) { };
    A(int, int) { };

};

Ans to your question

// .. is replacing T by A necessary at all?

Yes. Because not replacing it with a specialized type will refer to it as partial specialization. Friend declarations are not allowed to refer to a partial specialization. Only full specializations are allowed.

ritesh sangani
  • 280
  • 3
  • 8