The rules for legal placement of explicit specializations as per [temp.expl.spec] are not entirely easy to grasp, particularly when mixing with hidden friends, so as to avoid the immolation[temp.expl.spec]/8 of my specializations and friend functions I wish a second opinion(1) on the following:
- Is it legal to define, in a header file of a class template, a hidden friend in an explicit specialization of an otherwise non-defined (/only declared) member class of the class template? Assume that this header is included in at least two different translation units.
(1) My own interpretation of the standard says that the answer is "yes; this is legal", as per the standard references below.
Or, with an example: is the following program well-formed?
// s.h
#pragma once
template <int N>
struct S {
struct M;
};
template<>
struct S<42>::M {
friend int f(M) { return 42; }
};
// foo.h
#pragma once
void foo();
// foo.cpp
#include "foo.h"
#include <iostream>
#include "s.h"
void foo() {
std::cout << f(S<42>::M{});
}
// main.cpp
#include <iostream>
#include "foo.h"
#include "s.h"
int main() {
std::cout << f(S<42>::M{});
foo();
}
Note that friends defined at their friend declarations are inline as per [class.friend]/7, so whilst I'm somewhat worried about ODR-violations it is not for the friend but rather for the member class specialization. I think [basic.def.odr]/6 applies here if we consider S<42>::M as "just" a class type.
Clang and GCC both accepts the above, but this may not be significant in case the program is ill-formed NDR or has undefined behaviour. GCC 10.1.0 DEMO, Clang 10.0.0 DEMO.