Reputation: 830
I have a template struct SFoo
that contains a member struct SZug
:
template <typename tTYPE>
struct SFoo
{
struct SZug {};
};
I have another struct SBar
that takes a type parameter:
template <typename tTYPE>
struct SBar
{ /* stuff */ };
I would like to specialize SBar
using SZug
for the type parameter, like so:
template <typename tTYPE>
struct SBar<typename SFoo<tTYPE>::SZug>
{ /* different stuff */ };
This doesn't compile - LLVM outputs:
non-deducible template parameter 'tTYPE'
While a compiler could easily deduce this if it wished, I'm guessing it's just that the C++ spec would need to specifically cover this case.
Is there any way to achieve this?
(note: I'm currently working around it by moving SZug
outside of SFoo
and using a using
declaration, but it's ugly.)
Upvotes: 2
Views: 358
Reputation: 37606
I am not sure I fully understood what you want to do, but you could try the following (it only requires adding a specific attributes to SZug
:
template <typename tTYPE>
struct SFoo {
struct SZug {
// Add this to be able to obtain SFoo<T> from SFoo<T>::SZug
using type = tTYPE;
};
};
Then a small template to check if a type is a SFoo<T>::SZug
:
template <typename tTYPE, typename Enabler = void>
struct is_SZug: public std::false_type { };
template <typename tTYPE>
struct is_SZug<tTYPE, typename std::enable_if<
std::is_same<tTYPE, typename SFoo<typename tTYPE::type>::SZug>{}
>::type>: public std::true_type { };
And a slight modification to the SBar
template to enable the "specialization" if the type is a SZug
:
template <typename tTYPE, typename Enabler = void>
struct SBar
{ static void g(); };
template <typename tTYPE>
struct SBar<tTYPE, typename std::enable_if<is_SZug<tTYPE>{}>::type>
{ static void f(); };
A little check:
void f () {
SBar<int>::g();
SBar<SFoo<int>::SZug>::f();
}
Note: You could also directly set SFoo<T>
as the type
attribute in SFoo<T>::SZug
, you would simply need to change the second argument of std::is_same
a little.
Upvotes: 2
Reputation: 76297
You can get the effect for which you're looking through the following (which prints out 0 1, BTW):
#include <type_traits>
#include <iostream>
namespace detail
{
struct SZugBase{};
}
template <typename tTYPE>
struct SFoo
{
struct SZug : public detail::SZugBase {};
};
template<typename tType, bool IsFoo>
struct SBarBase
{
int value = 0;
};
template<typename tType>
struct SBarBase<tType, true>
{
int value = 1;
};
template <typename tTYPE>
struct SBar : public SBarBase<tTYPE, std::is_convertible<tTYPE, detail::SZugBase>::value>
{ /* stuff */ };
int main()
{
SBar<int> b0;
SBar<SFoo<int>::SZug> b1;
std::cout << b0.value << " " << b1.value << std::endl;
}
Explanation
First, we give SZug
a regular-class base:
namespace detail
{
struct SZugBase{};
}
template <typename tTYPE>
struct SFoo
{
struct SZug : public detail::SZugBase {};
};
Note the following:
SZugBase
is not parameterized by anything, so it is easy to refer to it independently of the parameter of SFoo
SZugBase
is in a detail
namespace, so, by common C++ conventions, you're telling clients of your code to ignore it.
Now we give SBar
two base classes, specialized on whether something is convertible to the non-template base of SZug
:
template<typename tType, bool IsFoo>
struct SBarBase
{
int value = 0;
};
template<typename tType>
struct SBarBase<tType, true>
{
int value = 1;
};
Finally, we just need to make SBar
a subclass of these bases (depending on the specialization):
template <typename tTYPE>
struct SBar : public SBarBase<tTYPE, std::is_convertible<tTYPE, detail::SZugBase>::value>
{ /* stuff */ };
Note that you don't specialize SBar
here, you rather specialize the base classes. This effectively gives the same effect, though.
Upvotes: 1