ClassWithTemplateClass<enumVal>::
is a nested name specifier which in turn is a non-deduced context. Since enumVal
is a template parameter that appears to the left of a scope resolution operator ::
, its value cannot be deduced by the compiler.
The <<
operator can be defined (1) as a friend inside class TemplateClass
:
enum MyEnum { X, Y, Z };
template <MyEnum E>
struct ClassWithTemplateClass
{
template <bool B>
struct TemplateClass
{
friend auto& operator<<(TemplateClass& a, int b)
{
return a;
}
};
};
where TemplateClass
always refers to a particular instantiation of ClassWithTemplateClass<?>::TemplateClass<?>
DEMO
or (2) inside ClassWithTemplateClass
:
enum MyEnum { X, Y, Z };
template <MyEnum E>
struct ClassWithTemplateClass
{
template <bool B>
struct TemplateClass
{
};
template <bool B>
friend auto& operator<<(TemplateClass<B>& a, int b)
{
return a;
}
};
DEMO 2
or, (3) you could provide a separate operator definition for each pre-defined enum value (though it can have more values than defined as constants), so that only B
needs to be deduced:
enum MyEnum { X, Y, Z };
template <MyEnum E>
struct ClassWithTemplateClass
{
template <bool B>
struct TemplateClass
{
};
};
template <bool B>
auto& operator<<(ClassWithTemplateClass<X>::TemplateClass<B>& a, int b)
{
return a;
}
template <bool B>
auto& operator<<(ClassWithTemplateClass<Y>::TemplateClass<B>& a, int b)
{
return a;
}
template <bool B>
auto& operator<<(ClassWithTemplateClass<Z>::TemplateClass<B>& a, int b)
{
return a;
}
DEMO 3
, or (4) store template parameters as static data members of TemplateClass
, and use them to both make the operator SFINAE-able and to retrieve their values:
enum MyEnum { X, Y, Z };
template <MyEnum E>
struct ClassWithTemplateClass
{
template <bool B>
struct TemplateClass
{
static constexpr MyEnum ClassWithTemplateClass_E = E;
static constexpr bool TemplateClass_B = B;
};
};
template <typename T
, MyEnum E = T::ClassWithTemplateClass_E
, bool B = T::TemplateClass_B>
auto& operator<<(T& a, int b)
{
return a;
}
DEMO 4
As another alternative, (5) you can dispatch the call from operator<<
to another function, and explicitly specify its template arguments, so that the signature is exactly as you wanted, and the template parameters are known:
enum MyEnum { X, Y, Z };
template <MyEnum E>
struct ClassWithTemplateClass;
template <MyEnum E, bool B>
auto& print(typename ClassWithTemplateClass<E>::template TemplateClass<B>& a, int b);
template <MyEnum E>
struct ClassWithTemplateClass
{
template <bool B>
struct TemplateClass
{
friend auto& operator<<(TemplateClass& a, int b)
{
return print<E, B>(a, b);
}
};
};
template <MyEnum E, bool B>
auto& print(typename ClassWithTemplateClass<E>::template TemplateClass<B>& a, int b)
{
return a;
}
DEMO 5