Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Declaring a constexpr specialization as friend

I have a templated class A and a templated function f which returns A objects. I want f<T> to be a friend of A<T> and still be constexpr

template <typename T>
class A;

template <typename T>
constexpr A<T> f();

//f<T> is a friend of A<T>

template <typename T>
class A {
  friend /* constexpr? */ A f<T>();
  constexpr A() {}
};

template <typename T>
constexpr A<T> f() { return {}; }

int main() {
  constexpr auto a  = f<void>();
}

I can't get clang and gcc to agree on what's right here. If I don't put constexpr in the friend declaration, gcc works fine but clang won't compile it, erroring with:

main.cpp:18:18: error: constexpr variable 'a' must be initialized by a constant expression
  constexpr auto a  = f<void>();
                 ^    ~~~~~~~~~
main.cpp:18:23: note: non-constexpr function 'f<void>' cannot be used in a constant expression
  constexpr auto a  = f<void>();
                      ^
main.cpp:9:12: note: declared here
  friend A f<T>(); 

If I mark it as constexpr in the friend declaration, clang compiles fine but gcc gives me the error:

main.cpp:9:27: error: 'constexpr' is not allowed in declaration of friend template specialization 'A<T> f<T>()'
   friend constexpr A f<T>();

How can I make everyone happy?

like image 888
Ryan Haining Avatar asked Aug 24 '15 03:08

Ryan Haining


1 Answers

int main() { constexpr auto a  = f<void>(); }

This specializes the function template f as the function f<void>(); during the specialization of f, the compiler also attempts to instantiate A<void>, which in turn declares the specialization friend f<void>().

These two declarations must match for constexpr:

[dcl.constexpr] / 1

[...] If any declaration of a function or function template has a constexpr specifier, then all its declarations shall contain the constexpr specifier. [Note: An explicit specialization can differ from the template declaration with respect to the constexpr specifier. —end note]

Clang should probably be erroring out earlier when you omit constexpr in the friend declaration rather than stamping out what appears to be a non-constexpr function, but at least it accepts the correct syntax.

Gcc shouldn't allow the version missing constexpr, and gives an error when you do provide constexpr due to a bug. This has since been fixed in the trunk and I can confirm it works now, although it still does not provide an error when constexpr is missing.

like image 163
user657267 Avatar answered Sep 22 '22 02:09

user657267