Re-factoring legacy code I want to merge separate template classes/structs that are related to each other (to avoid namespace pollution).
Nested
(below) is a helper class for MyStruct
, which i want to move into MyStruct
.
But I cannot make this work:
#include <type_traits>
#include <iostream>
struct YES {} ;
struct NO {};
template <typename TYPE>
struct MyStruct
{
template <typename TYPE_AGAIN = TYPE, typename SELECTOR = NO>
struct Nested
{
static void Print(void)
{
std::cout << "MyStruct::Nested<bool = false>::Print()" << std::endl;
}
};
template <>
struct Nested<TYPE, typename std::enable_if<std::is_integral<TYPE>::value, YES>::type>
{
static void Print(void)
{
std::cout << "MyStruct::Nested<bool = true>::Print()" << std::endl;
}
};
};
the compiler complains:
g++ -O0 -g3 -Wall -c -fmessage-length=0 -std=c++11 -MMD -MP -MF"main.d" -MT"main.d" -o "main.o" "../main.cpp"
In file included from ../main.cpp:8:0:
../MyStruct.h:31:12: error: explicit specialization in non-namespace scope ‘struct MyStruct<TYPE>’
template <>
^
make: *** [main.o] Error 1
Actually it also bothers me to have to include
<typename TYPE_AGAIN = TYPE>
But without, there are even more complains:
g++ -O0 -g3 -Wall -c -fmessage-length=0 -std=c++11 -MMD -MP -MF"main.d" -MT"main.d" -o "main.o" "../main.cpp"
In file included from ../main.cpp:8:0:
../MyStruct.h:31:12: error: explicit specialization in non-namespace scope ‘struct MyStruct<TYPE>’
template <>
^
../MyStruct.h:32:9: error: template parameters not used in partial specialization:
struct Nested<typename std::enable_if<std::is_integral<TYPE>::value, YES>::type>
^
../MyStruct.h:32:9: error: ‘TYPE’
make: *** [main.o] Error 1
You can't specialize templates in non-namespace scope, like a struct
in your case.
You would have to put the specializations outside of the struct definition:
template<typename TYPE> template<>
struct MyStruct<TYPE>::Nested<...> {};
But now you have another problem, if you want to specialize a template inside of a template class, you'll have to specialize it for every template class. You can't just specialize one member function, you have to specialize the whole class.
So, you need to do this:
template<> template<>
struct MyStruct<int>::Nested<...> {};
Also, you really don't need SFINAE for this:
template<typename SELECTOR>
struct Nested; // Default invalid SELECTOR
template<>
struct Nested<YES> { /*...*/ };
template<>
struct Nested<NO> { /*...*/ };
It seems there are 2 reasons why you can not compile your code:
a full specialization of a inner template class can not be declared inside the class. The best solution is to add a dumb defaulted template parameter, and then declare a partial specialization of the inner template class.
A template specialization can not directly be disabled with std::enable_if
. But we can use the void_t
trick to do it. void_t
is a c++17 feature, so with earlier c++ standard we need to provides its definition. I took the void_t
definition from http://en.cppreference.com/w/cpp/types/void_t.
So you could rewrite your code as follow:
#include <type_traits>
#include <iostream>
#include <type_traits>
//Crédit: cpp-reference.com
template<typename... Ts> struct make_void { typedef void type;};
template<typename... Ts> using void_t = typename make_void<Ts...>::type;
template <typename TYPE>
struct MyStruct
{
//The third parameter is an unused parameter
template <typename TYPE_AGAIN = TYPE, typename SELECTOR = void
, typename = void>
struct Nested
{
static void Print(void)
{
std::cout << "MyStruct::Nested<bool = false>::Print()" << std::endl;
}
};
//We declare a partial specialization, T go to the third parameter. We use the void_t trick here.
template <class T>
struct Nested<TYPE,
void_t<typename std::enable_if<
std::is_integral<TYPE>::value>::type>
,T>
{
static void Print(void)
{
std::cout << "MyStruct::Nested<bool = true>::Print()" << std::endl;
}
};
};
int main(){
MyStruct<int>::Nested<>::Print();
return EXIT_SUCCESS;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With