3

I attempted to get friend name injection to work with the following snippet:

struct foo
{
  friend foo f() { return {}; }
};

int main()
{
  auto x = f();
  return 0;
}

This fails to compile:

test.cc:8:10: error: use of undeclared identifier 'f'
  auto x = f();
           ^
1 error generated.

I have to declare f in the outer namespace for the error to vanish.

The standard has an explanation in paragraph 3 of §7.3.1.2:

If a friend declaration in a non-local class first declares a class, function, class template or function template97 the friend is a member of the innermost enclosing namespace. The friend declaration does not by itself make the name visible to unqualified lookup (3.4.1) or qualified lookup (3.4.3). [Note: The name of the friend will be visible in its namespace if a matching declaration is provided at namespace scope (either before or after the class definition granting friendship). — end note ]

My question: why do we need an extra declaration of f at namespace scope for this to compile? Does it have to do with ADL? Is there a (hacky) way around this requirement?

mavam
  • 12,242
  • 10
  • 53
  • 87

2 Answers2

4

You can force ADL:

struct foo {
  friend foo f(foo *) { return {}; }
};

int main()
{
  auto x = f((foo *) 0);
  return 0;
}

Compiles with g++ 4.9.0 and clang++ 3.4. Of course, might not be practical.

ADDENDUM: Thanks to Richard Hodges, here is another possible workaround, but it might be a g++ 4.9.0 bug. There are differences between clang++ and g++. I'll try to look into what the standard says, if I have time. If the OP wants to post this as a new question asking about which compiler is wrong, please do.

struct foo {
  friend foo f1() { return {}; }
  friend foo f2(foo *) { return {}; }
  template<typename T = void>
  friend foo f3() { return {}; }
};

int main()
{
  auto x1 = f1(); // Error, f1() not visible.
  auto x2 = f2((foo *) 0); // Force ADL.
  auto x3 = f3<void>(); // Use template, okay with g++, fails with clang++.
  auto x4 = f3(); // Use template with default param, okay with g++, fails with clang++.
  return 0;
}
kec
  • 2,099
  • 11
  • 17
  • struct foo { template friend foo f() { return {}; } }; int main() { auto x = f(); return 0; } also compiles - equally horrible. – Richard Hodges May 04 '14 at 01:25
  • I wonder if it's possible to add a bit more template magic to make `f3` work with generically. – mavam May 04 '14 at 01:41
  • @MatthiasVallentin: Not sure if you saw the version with the default template argument or not, since I was making some edits. That one looks like it works transparently, but might be a g++ bug. – kec May 04 '14 at 01:43
  • Okay, a quick search of the standard didn't turn up anything. It seems like a g++ bug. So the question is whether or not a template friend function definition is available for lookup without a matching declaration in namespace scope. Compilers disagree. Post this as a new question, if you want a definitive answer. – kec May 04 '14 at 01:52
  • @MatthiasVallentin: Here is the new question: http://stackoverflow.com/questions/23452015/is-a-friend-function-template-defined-in-the-class-available-for-lookup-clang – kec May 04 '14 at 02:43
-2

friend works on a declaration, not a definition.

struct foo
{
  friend foo f();
};

foo f() { return {}; }

int main()
{
  auto x = f();
  return 0;
}

compiles

Richard Hodges
  • 68,278
  • 7
  • 90
  • 142