How can I specialise a trait using a type when it's definition is nested in a template type ?
Here is an example of what I'm doing and what I want:
// A trait that can be specialized
template<typename T>
struct MyTrait
{
static constexpr int value = 0;
};
//This base case works fine
struct ClassicStruct
{
struct ClassicNestedType {};
};
//Inside this template struct I defined a Type
template<typename T>
struct TemplateStruct
{
struct DefinedInTemplate
{
T val;
};
};
//=======THIS IS WHAT I WANT TO DO, and it doesn't compile ==========
template<typename T>
struct MyTrait<TemplateStruct<T>::DefinedInTemplate>
{
static constexpr int value = 589;
};
//This works fine
template<>
struct MyTrait<bool>
{
static constexpr int value = 4;
};
//perfectly fine too
template<typename T>
struct MyTrait<TemplateStruct<T>>
{
static constexpr int value = 254;
};
//Another example that is perfectly fine
template<>
struct MyTrait<ClassicStruct::ClassicNestedType>
{
static constexpr int value = 468;
};
int main()
{
printf("value of main: %d \n", MyTrait<TemplateStruct<int>>::value);//prints 254
printf("value of main: %d \n", MyTrait<TemplateStruct<int>::DefinedInTemplate>::value); //I wish it would print 589 here
printf("value of main: %d \n", MyTrait<ClassicStruct::ClassicNestedType>::value);//prints 468
}
in GCC the compile error is
type/value mismatch at argument 1 in template parameter list for 'template struct MyTrait' 27 | struct MyTrait<TemplateStruct::DefinedInTemplate>
That error is weird, TemplateStruct<T>::DefinedInTemplate
is a type no ?
It should be noted that in my real case I can't replace the trait by a concept or something else. This is in UE5 where I must specialise an existing trait. So I can't get around specialising a trait, and I can't really modify that trait, as I'm trying to avoid editing the engine code. This trait is used a lot and I wouldn't be very comfortable touching it.
This is because the T
in TemplateStruct<T>::DefinedInTemplate
is in a non-deduced context. Given just any type, we can't tell if it is TemplateStruct<T>::DefinedInTemplate
for some T
directly with existing C++ tools. It may be possible with C++ reflections in future versions.
A C++20 solution is to make a concept and partially specialise based on that:
template<typename T>
concept IsDefinedInTemplateInsideTemplateStruct =
std::same_as<T, typename TemplateStruct<decltype(T::val)>::DefinedInTemplate>;
template<IsDefinedInTemplateInsideTemplateStruct T>
struct MyTrait<T>
{
static constexpr int value = 589;
};
A C++11 solution would be to make DefinedInTemplate
an alias for some external template:
namespace impl {
template<typename T>
struct TemplateStructInnerClass {
T val;
};
}
template<typename T>
struct TemplateStruct {
using DefinedInTemplate = impl::TemplateStructInnerClass<T>;
};
template<typename T>
struct MyTrait<impl::TemplateStructInnerClass<T>>
{
static constexpr int value = 589;
};