Compilers (clang-5.0.0, GCC-7.3, ICC-18 and MSVC-19) diverge w.r.t. accessibility of members of A below.
class A {
template <class> static constexpr int f() { return 0; }
template <int> struct B {};
template <class T> using C = B<f<T>()>;
};
Indeed, consider the following usages:
template <class T> using D = A::C<T>;
int main() {
// | clang | gcc | icc | msvc
(void) A::f<int>(); // 1: | f | f | f | f, (C)
(void) A::B<0>{}; // 2: | B | | B | B, (C)
(void) A::C<int>{}; // 3: | C, f | | C | C
(void) D<int>{}; // 4: | f | | C | C
}
The table on the right shows which members each compiler requires to be made public to accept the code (when compiled for C++14).
IMHO, ICC and MSVC (ignoring (C) entries) look correct. Except for the first line, GCC seems to be completely ignoring accessibility.
I disagree with clang when it requires f to be public to instantiate A::C<int> and D<int>. Like ICC and MSVC, I think C and only C needs to be public. It is true that C uses f but is it not an implementation detail? Notice that C also uses B. If clang were correct, then why does it not require B to be public as well?
Finally, let us consider the (C) entries. MSVC requires C to be public when it first encounters the definition of D, that is, MSVC complains about C being private.
My questions are:
- Am I right (and so is ICC) in my analysis? Otherwise which other compiler is correct and why?
- Is the MSVC issue yet another incarnation of two-phase instantiation bug in msvc?
Update: Regarding GCC, this seems to be the bug reported in comment 8, here.