1

Recently I started to use template. And I just dump in a compile error.

I have a Class Template Foo which take a boolean B as a template parameter. And I'm trying to make friend, in an instentiation of Foo with template argument b, the Constructor of the other instantiation of Foo with argument !b with this code:

template<bool B>
class Foo
{
public:
    Foo(Foo<!B>&);
private:
    friend Foo<!B>::Foo(Foo<B>&);
};

The compiler return me these errors:

test.cpp:22:18: error: C++ requires a type specifier for all declarations
        friend Foo<!B>::Foo(Foo<B>&);
        ~~~~~~          ^
test.cpp:22:18: error: constructor cannot have a return type
        friend Foo<!B>::Foo(Foo<B>&);
                        ^~~
2 errors generated.

I compiled it with the following command: clang++ -std=c++11 foo.cpp on Windows.

I really don't understand how to make it work, any help to understand what's wrong is appreciated.

Poroing
  • 61
  • 7

1 Answers1

4

I was commenting on someone else's answer and they asked me to paste my analysis here.

I think that your case is problematic because Foo<true> has a friend declaration to Foo<false>'s member function which in turn refers to Foo<true>'s member function. While the members respectively are declared before the member functions, this kind of circular reference is not supported by compilers. GCC complains at instantiation

main.cpp: In instantiation of 'class Foo<false>':
main.cpp:9:12:   required from 'class Foo<true>'
main.cpp:15:49:   required from here
main.cpp:9:12: error: invalid use of incomplete type 'class Foo<true>'
    friend Foo<!B>::Foo(Foo<B>&);

This error message is a bit confusing, because it suggests that incomplete types could not be used with ::, which is wrong. A class that is being defined can be used with :: even if it is incomplete yet. A class however that is being instantiated is handled differently by GCC than a class that is being defined, and refering back to the class that is being instantiated in a qualified name is not supported by GCC apparently.

We have a DR about such cyclic references, http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#287 which would appear to make this legal, but as the final statement notes, "This needs work", so GCC is in its right to reject your code I would say. To add to that confusion, we also have an explicit rule for names used in friend function declarations, which isn't mentioned in DR287 and its "as if" would make your code ill-formed because Foo<!B>'s point of instantiation is immediately before the point of instantiation of Foo<B> and unless we apply the rule in DR287 cannot see the constructor declaration of Foo<B>.

Friend classes or functions can be declared within a class template. When a template is instantiated, the names of its friends are treated as if the specialization had been explicitly declared at its point of instantiation


Independent of this, there's another issue at hand: Is Foo<!B>::Foo(Foo<B>&); referring to a constructor? It is a reference to the constructor if it refers to the injected class name, which the compiler then must treat as the constructor in these constexts. So the compiler must resolve Foo<!B>::Foo. But Foo<!B> is a dependent type, so the compiler cannot look into it to decide what kind of name it is (remember that struct A { int A; } is allowed, and the compiler has to verify it is not in such a situation). I think for these reasons, cland fails to parse your template because it is confused as to what friend Foo<!B>::Foo(Foo<B>&) means.

I believe the template itself (disregarding issues of instantiation) should be well-formed because the compiler could just delay the lookup of Foo<!B>::Foo and take it as a non-type which it actually is (constructors).

Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
  • Thank you a lot for you're greatly informative response. So my only way to go is still to declare the whole Class template instentiation as friend, right? – Poroing Aug 29 '15 at 12:41
  • @user correct, I think so. But perhaps you can move the code that needs private access to a member function and befriend the member function – Johannes Schaub - litb Aug 29 '15 at 13:09
  • Oh, I like you're idea of moving the code in another member function, I'll see if I can do something that looks good. Thank's you very much. – Poroing Aug 29 '15 at 13:13