Search code examples
c++templatesc++20friend

How can I fix a C++20 compile error involving concepts and friends?


Let's say I start with this simple example of the use of a C++20 "concept":

template <typename T>
concept HasFoo = requires( T t )
{
    t.foo();
};

template <HasFoo T>
void DoFoo( T& thing )
{
    thing.foo();
}

class FooThing
{
public:
    void    foo() {}
};

int main(int argc, const char * argv[]) {
    FooThing x;
    DoFoo( x );
    
    return 0;
}

This compiles, and the concept verifies that the FooThing class has a method foo. But suppose I want to make the method foo private, and call DoFoo on the FooThing from another method of FooThing. So I try adding a friend declaration:

class FooThing
{
private:
    void    foo() {}
    
    friend void DoFoo<FooThing>( FooThing& thing );
};

This results in an error message: FooThing does not satisfy HasFoo because t.foo() would be invalid: member access into incomplete type FooThing.

To reassure myself that the concept really is essential to this problem, I tried doing without it,

template <typename T>
void DoFoo( T& thing )
{
    thing.foo();
}

and then the error goes away. Is there any way to fix the error while keeping the concept?


If I try the suggestion of an attempted answer and add a forward declaration

template<typename T> void DoFoo(T&);

before the class, then the compile error goes away, but I get a link error saying that DoFoo(FooThing&) or DoFoo<FooThing>(FooThing&) is an undefined symbol.


Solution

  • Yes! This friend declaration compiles and links:

    class FooThing
    {
    //public:
        void    foo() {}    
    
        template<HasFoo T>
        friend void DoFoo(T&);
    };
    

    I would have to poke around to find the exact standardese, but I know there is a rule that multiple declarations referring to the same template must have the same constraints (typename T is not allowed here).