2

Problem

Here's a contrived example of a problem I'm facing. I have a templated object with a map function that creates a new object and operates on private (or protected, it doesn't matter) members within that new object.

template<typename T>
class Foo {
public:
    template<typename R>
    Foo<R> map(std::function<R(std::optional<T>)> &&function) {
        auto mappedValue = function(mValue);
        
        Foo<R> f{};
        f.mValue = mappedValue;
    }
    
private:
    std::optional<T> mValue;
};

This works fine so long as T and R are identical. For example:

int main()
{
    Foo<int> f1{};
    Foo<int> f2 = f1.map<int>([](std::optional<int> value) {
        if (value.has_value()) {
            return value.value() + 1;   
        }
        else {
            return 1;
        }
    });

    return 0;
}

However, the moment I T != R, I run into problems:

int main()
{
    Foo<int> f1{};
    Foo<double> f2 = f1.map<double>([](std::optional<int> value) {
        if (value.has_value()) {
            return value.value() + 1.0;   
        }
        else {
            return 1.0;
        }
    });

    return 0;
}
main.cpp:22:11: error: ‘std::optional Foo::mValue’ is private within this context
         f.mValue = mappedValue;
         ~~^~~~~~
main.cpp:26:22: note: declared private here
     std::optional<T> mValue;
                      ^~~~~~

Question

This is easily understood. C++ access control works on a per-class basis and Foo<int> is not the same class as Foo<double>. I can even fix the problem by adding: friend class Foo<int>. That doesn't scale well, since I don't know what T and R may be.

Does anyone know of a generic way to handle this problem and give Foo<int> access to Foo<double>'s private/protected members?

Quentamia
  • 3,244
  • 3
  • 33
  • 42
  • I think what you are looping for is: https://stackoverflow.com/questions/3292795/how-to-declare-a-templated-struct-class-as-a-friend right? – Ralf Ulrich Jun 04 '21 at 22:42

1 Answers1

2

I figured it out.

You need to declare your current class as a friend of itself.

template<typename T>
friend class Foo;

The entire thing looks like this:

template<typename T>
class Foo {
public:
    template<typename R>
    Foo<R> map(std::function<R(std::optional<T>)> &&function) {
        auto mappedValue = function(mValue);
        
        Foo<R> f{};
        f.mValue = mappedValue;
    }
    
private:
    std::optional<T> mValue;

    template<typename T>
    friend class Foo;
};
Quentamia
  • 3,244
  • 3
  • 33
  • 42