I have two enums
#include <iostream>
enum class E1 : unsigned int
{
E11 = 1
};
enum class E2 : unsigned int
{
E21 = 1
};
which have identical underlying values (1
) in this case. Next, I have a class C
which has two template parameters, an integer j
and a value i
with type auto
.
template<int j, auto i>
struct C
{ C() { std::cout << "none\n"; } };
I want to partially specialize this class for both E1::E11
and E2::E21
like this:
template<int j>
struct C<j, E1::E11>
{ C() { std::cout << j << "E11\n"; } };
template<int j>
struct C<j, E2::E21>
{ C() { std::cout << j << "E21\n"; } };
And for completeness sake here is main that instantiates two objects:
int main()
{
C<0,E1::E11> e11;
C<1,E2::E21> e21;
return 0;
}
The code above works absolutely fine on gcc and icc as can be verified on Godbolt (full code)
But it fails with any other compiler (clang, msvc). With the following message
<source>:27:18: error: ambiguous partial specializations of 'C<0, E1::E11>'
C<0,E1::E11> e11;
^
<source>:18:8: note: partial specialization matches [with j = 0]
struct C<j, E1::E11>
^
<source>:22:8: note: partial specialization matches [with j = 0]
struct C<j, E2::E21>
It's kind of clear to me why this happens. The question that I can't seem to answer though is whether it is possible to solve this in a standard compatible way (if this is some gcc or icc feature) or if there's a workaround for the failing compilers (clang, msvc).
Btw. if I remove the int j
template argument and just leave the auto i
the code compiles with all compilers.
Thanks in advance,
Arno
CodePudding user response:
The auto
template argument can be replaced by
template<int j,typename T,T i>
struct C
{ C() { std::cout << "none\n"; } };
If you are fine with more typing you can explicitly specify the type of the enum:
#include <iostream>
enum class E1 : unsigned int
{
E11 = 1
};
enum class E2 : unsigned int
{
E21 = 1
};
template<int j,typename T,T i>
struct C
{ C() { std::cout << "none\n"; } };
template<int j>
struct C<j,E1, E1::E11>
{ C() { std::cout << j << "E11\n"; } };
template<int j>
struct C<j,E2, E2::E21>
{ C() { std::cout << j << "E21\n"; } };
int main()
{
C<0,E1,E1::E11> e11;
C<1,E2,E2::E21> e21;
return 0;
}
And for less typing you can use a helper function:
template <int j,auto i>
auto make_C(){
return C<j,decltype(i),i>();
}
int main()
{
auto e11 = make_C<0,E1::E11>();
auto e21 = make_C<1,E2::E21>();
}
... or a type trait:
template <int j,auto i>
struct C_helper {
using type = C<j,decltype(i),i>;
};
int main()
{
C_helper<0,E1::E11>::type e11;
C_helper<1,E2::E21>::type e21;
}
CodePudding user response:
msvc/clang seems to have issue with partial specialization in your cases :(
As workaround, you might split the parameter in 2 classes:
template <auto i>
struct EC
{
template <int j>
struct C
{
C() { std::cout << "none\n"; }
};
};
template <>
struct EC<E1::E11>
{
template <int j>
struct C
{
C() { std::cout << j << "E11\n"; }
};
};
// Same for E2::E21
and then
EC<E1::E11>::C<0> e11;
EC<E2::E21>::C<0> e21;