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.)
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.
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.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.