When using friend injection in the body of a function template, the side-effects of processing the body’s code aren’t visible right after instantiating the function.
template<int N>
struct FriendIndex {
friend constexpr bool Get(FriendIndex);
};
template<int N, bool val>
struct FriendInjector {
friend constexpr bool Get(FriendIndex<N>) {
return true;
}
};
template <bool val>
int Indirection()
{
(void)FriendInjector<0, val>{};
return 0;
}
int dummy = Indirection<true>();
static_assert(Get(FriendIndex<0>{}),""); // Error: Get() not defined yet.
However, if the function template returns auto instead, the friend injection is evaluated at instantiation, and the assert now succeeds (in GCC / Clang / MSVC).
template <bool val>
auto Indirection()
{
(void)FriendInjector<0, val>{};
return 0;
}
int dummy = Indirection<true>();
static_assert(Get(FriendIndex<0>{}),""); // Success!
Looking at the C++14 standard draft, the main explanation I see for this is in 7.1.6.4 [dcl.spec.auto]/12
:
Return type deduction for a function template with a placeholder in its declared type occurs when the
definition is instantiated even if the function body contains a return statement with a non-type-dependent
operand. [ Note: Therefore, any use of a specialization of the function template will cause an implicit
instantiation. Any errors that arise from this instantiation are not in the immediate context of the function
type and can result in the program being ill-formed. — end note ]
Does this mean there’s no guaranteeing when a function template body’s side-effects will be visible, beyond what’s needed for the return types? And that in my case, since (void)FriendInjector<0, val>{};
isn’t needed for return type deduction, it’s just a compiler choice to have the entire body be evaluated at instantiation?