Home > Blockchain >  How can I fix a C 20 compile error involving concepts and friends?
How can I fix a C 20 compile error involving concepts and friends?

Time:06-12

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 a suggestion of an attempted answer and change my friend declaration to

    template <HasFoo T>
    friend void DoFoo( FooThing& thing );

then I just get a different error message; instead of complaining that the concept is looking at an incomplete type, it complains that the concept is not satisfied "because t.foo() would be invalid: foo is a private member of FooThing"

If I try the suggestion of a different 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.

CodePudding user response:

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).

CodePudding user response:

The problem is that your concept check requires that T needs to be a complete type(because of the use of t.foo()) but at the point of the friend declaration the class FooThing is incomplete and so the check t.foo() is invalid. You can confirm this by using some other complete type in the friend declaration and the the concept will work as you intended it to. Demo

Is there any way to fix the error while keeping the concept?

As long as your check requires T to be a complete type this friend declaration involving FooThing can't work as at the point of the friend declaration the class in not complete yet.

  • Related