I'm using recursive templates classes that inherit recursively.
I'm trying to define an abstract way of getting the n-th base (like base 0 is the current class, base 1 is its base, base 2 is the base's base etc.).
(In this example the template parameters are size_t
but the same principle applies with typename
or class
.)
I can do this fine using a helper struct, partially specialized. But I want to make it independent of the template (and not make a helper struct whenever I have a recursive template), as follows:
namespace helper
{
template<template<size_t...> typename templ, size_t pos, size_t s0, size_t... rest>
struct getter
{
typedef typename getter<templ, pos - 1, rest...>::type type;
type &operator()(templ<s0, rest...> &s)
{
getter<templ, pos - 1, rest...> getter;
return getter(static_cast<templ<rest...>&> (s));
}
};
template<template<size_t...> typename templ, size_t s0, size_t... rest>
struct getter<templ, 0, s0, rest...>
{
typedef templ<s0, rest...> type;
type &operator()(templ<s0, rest...> &s)
{
return s;
}
};
}
Now, in my template class, I want to use this helper to make a function get<size_t n>()
that returns me a reference to the n-th base (code fails to compile at get
declaration):
template<size_t...>
struct record {};
template<size_t n, size_t... rest>
struct record<n, rest...> : record<rest...>, value<n>
{
template<size_t pos>
typename helper::getter<record, pos, n, rest...>::type::value_type &get()
{
helper::getter<record, pos, n, rest...> getter;
return static_cast<typename helper::getter<record, pos, n, rest...>::type::value_type&>(getter(*this));
}
};
This fails because, inside the record
template, record
is the final class and not the template. I would like to use something like "current template" instead of "currently instantiated class".
The only workaround I've found (which works under Visual Studio 2015) is to use a global alias to replicate the template:
template<size_t...s>
using g_record = record<s...>;
and then modify the get declaration to call the global alias (which points to the same type):
...
template<size_t pos>
typename helper::getter<g_record, pos, n, rest...>::type::value_type &get()
{
helper::getter<g_record, pos, n, rest...> getter;
return static_cast<typename helper::getter<g_record, pos, n, rest...>::type::value_type&>(getter(*this));
}
...
Is there a more direct or a "correct" way to do this?
Looks like I was a bit hasty in my digging.
Apparently using the template's fully qualified name works (in my example it's ::record
):
template<size_t pos>
typename helper::getter<::record, pos, n, rest...>::type::value_type &get()
{
helper::getter<::record, pos, n, rest...> getter;
return static_cast<typename helper::getter<::record, pos, n, rest...>::type::value_type&>(getter(*this));
}
It should not be necessary to use any tricks to get the compiler to realize you are using record
as a template name instead of a type name referring to the current instantiation. See [temp.local]/1, emphasis mine:
Like normal (non-template) classes, class templates have an injected-class-name (Clause 9). The injected-class-name can be used as a template-name or a type-name . When it is used with a template-argument-list , as a template-argument for a template template-parameter , or as the final identifier in the elaborated-type-specifier of a friend class template declaration, it refers to the class template itself . Otherwise, it is equivalent to the template-name followed by the template-parameters of the class template enclosed in
<>
.
The workaround you posted is correct, but it should not be needed; this is a bug in the compiler.
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.