8

I have seen a CRTP solution, which extracted the interface into the base class, and friended only one of the pack arguments per base class. Then the most derived class inherited all the friended base classes and implemented the interface.

I cannot use this approach, since I need to protect the assignment operator, which is not inherited.

Also, since the assignment operator has a defined signature with exactly one parameter, I cannot use a key pattern.

This is what I would like to have:

template <typename... F>
struct A {
protected:
    A& operator=(const SomeClass &other) {
        //...
    }
private:
    //I would like to do the following, but it does not work
    friend F...;
}

Is there a way to do what I am needing?

Community
  • 1
  • 1
LoPiTaL
  • 2,495
  • 16
  • 23
  • IDGI. What needs to be a friend of what? And why? What is `A`? What is `SomeClass`? What are `F...` in this example? – Lightness Races in Orbit Jul 20 '15 at 23:12
  • @LightnessRacesinOrbit `A` is self explanatory, is a class with protected `operator=` around which stands my question. `SomeClass` is just for the sake of completeness on the `operator=` signature. It refers to some other class, not related nor important for this example. `F...` is the parameter pack argument passed to the template, which I want to make friend to `A`. It may contain other unrelated classes, and unknown to `A`, but they should have access to `operator=`, as to other methods, which I have extracted using the interface pattern I talked about in my question. – LoPiTaL Jul 21 '15 at 06:43
  • Does this answer your question? [Using variadic templates to specify friend classes](https://stackoverflow.com/questions/23305999/using-variadic-templates-to-specify-friend-classes) – Quuxplusone Sep 26 '22 at 17:12
  • @Quuxplusone no, that did not answer the 7 year-old question. Actually, in my question, I already referred to that same question and said why it doesn't solve my issue, as it was slightly different than the one in that question, having to protect `operator=` also. – LoPiTaL Sep 26 '22 at 18:03

1 Answers1

7

Well, you can always play dirty. First, define a repetition macro:

#define REPEAT_2(M, N) M(N) M(N+1)
#define REPEAT_4(M, N)   REPEAT_2  (M, N) REPEAT_2(M, N+2)
#define REPEAT_8(M, N)   REPEAT_4  (M, N) REPEAT_4(M, N+4)
#define REPEAT_16(M, N)  REPEAT_8  (M, N) REPEAT_8(M, N+8)
#define REPEAT_32(M, N)  REPEAT_16 (M, N) REPEAT_16(M, N+16)
#define REPEAT_64(M, N)  REPEAT_32 (M, N) REPEAT_32(M, N+32)
#define REPEAT_128(M, N) REPEAT_64 (M, N) REPEAT_64(M, N+64)

Then place 128 friend declarations into a variadic class template of your choice:

template <typename... T>
class A
{
    #define FRIEND(N) friend std::tuple_element_t<
                       std::min((std::size_t)N+1, sizeof...(T)), std::tuple<void, T...>>;
    REPEAT_128(FRIEND, 0)

    static constexpr int i = 3;
};

struct X; struct Y; struct Z;
using ASpec = A<X, Y, Z>;
struct X {int i = ASpec::i;};
struct Y {int i = ASpec::i;};
struct Z {int i = ASpec::i;};

template class A<>; // Small test for empty pack

Demo. Credit to @dyp.

If you have access to Boost.Preprocessor, the entire thing can be written far more concisely using BOOST_PP_REPEAT.

Columbo
  • 60,038
  • 8
  • 155
  • 203
  • It works like a charm! Only problem is that it is written for c++14. As it is more or less easy to go back to c++11, you should state the necessary changes in orer to use it: replace tuple_element_t with tuple_element::type; and std::min with a constexpr version of your own, since the standard function, in c++11, is not constexpr. Aside from that, it works perfect, thanks. – LoPiTaL Jul 20 '15 at 13:37