Interdependent class template and std::is_base_of specialization - c++

I am mildy confused by the following situation where I have a specialization enabled on is_base_of.
is_base_of requires the full definition of the type that is being checked to be available. However, the type that is being specialized is being used as a member of the type who's base is being checked - so both need to be defined before the other, and I cannot forward declare the inheritance relationship.
What is confusing is that if I instead tag the base and enable on this tag existing, it works. Surely for this to work, the inheritance relationship must be known at this point. so why doesn't is_base_of work without the full definition being available?
#define OPTION 2 // OPTION 2 : broken, OPTION 1 : works
#include <iostream>
#include <type_traits>
using namespace std;
template <typename T,typename Enable=void>
struct child;
template <typename T>
struct base
{
typedef T type;
#if OPTION ==1
struct base_tag{};
#endif
};
#if OPTION ==2
template <typename T>
struct child < T, typename std::enable_if < std::is_base_of< base<typename T::type>, T>::value>::type>
{
const char* value = "specialization";
};
#else
template <typename T>
struct child < T, std::void_t<typename T::base_tag> >
{
const char* value = "specialization";
};
#endif
template <typename T>
struct dervived : base<T>
{
child<dervived> child_;
typedef T type;
};
int main() {
std::cout << dervived<int>().child_.value << std::endl;
return 0;
}
DEMO

std::is_base_of requires complete type. which is not the case in
template <typename T>
struct derived : base<T>
{
child<derived> child_; // derived<T> not yet complete here.
typedef T type;
};
For T::base_tag, IIRC (I think POI of child<derived> move from before struct derived to current location in the class), T doesn't need to be complete, and only visited part of it is visible.
So derived::type would not be visible. (but derived::base::type would be).

Related

Detect whether a type is a vector of enum

I get an error:
error: default template arguments may not be used in partial specializations
in the following code:
#include <iostream>
#include <type_traits>
#include <vector>
enum class MyEnum
{
aaa,
bbb,
};
template<class T>
struct is_vector_enum
{
using type = T ;
constexpr static bool value = false;
};
template<class T, class std::enable_if<std::is_enum<T>::value>::type* = nullptr> // Error ....
struct is_vector_enum<std::vector<T>>
{
using type = std::vector<T> ;
constexpr static bool value = true;
};
int main()
{
std::cout << "is_vector_enum: " << is_vector_enum<std::vector<MyEnum>>::value << std::endl;
}
The purpose is to detect whether a type is a vector of enum.
How should I fix this code?
Your primary template and your specialization need to have the same number of template parameters. At the moment, your primary has 1:
template<class T>
struct is_vector_enum
and your specialization has 2:
template<class T, class std::enable_if<std::is_enum<T>::value>::type* = nullptr>
struct is_vector_enum<std::vector<T>>
The typical way to do this in C++17 is to provide a dummy 2nd template parameter to the primary, that defaults to void, to then let you do the SFINAE in the second parameter:
template <class T, class Enable=void>
struct is_vector_enum { /* ... */ };
template <class T>
struct is_vector_enum<std::vector<T>, std::enable_if_t<std::is_enum_v<T>>> { /* ... */ };
A different way entirely to do this would be:
template <typename T, std::enable_if_t<std::is_enum_v<T>, int> = 0>
std::true_type impl(std::vector<T> const&);
template <typename T>
std::false_type impl(T const&);
template <typename U>
using is_vector_enum = decltype(impl(std::declval<T>()));
Note that the impl functions here are not defined, and are not intended to be invoked.
Specializations are allowed to have a different number of template parameters than the primary. In fact, this happens quite often. However, as the error indicates, you are not allowed to give any of them default arguments.
That aside, I prefer simplicity, when possible.
template <typename T>
struct is_vector_enum : std::false_type { };
template <typename T>
struct is_vector_enum<std::vector<T>> : std::is_enum<T> { };

C++: Partially specializing template's type parameter as another template class's member-type

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.

How to specialize template on arbitrary dependent type

Say, I have some template which specialized for several types, TypeMathcer, which has type member.
#include <memory>
#include <vector>
template <typename T>
struct TypeMatcher;
template <typename T>
struct TypeMatcher<T *>
{
// making some type from T
typedef std::shared_ptr<T> type;
};
template <typename T>
struct TypeMatcher<T&>
{
// making other type from T
typedef std::vector<T> type;
};
Now, I want to create another template and specialize it for types I get from TypeMatcher. If I do it straightforward, like this
template <typename T>
struct MyNeedfullTemplate;
template <typename T>
struct MyNeedfullTemplate<typename TypeMatcher<T>::type>
{
};
I get compiler error: template parameters not deducible in partial specialization.
Same error if use using syntax
template <typename T>
using type_matcher_t = typename TypeMatcher<T>::type;
template <typename T>
struct MyNeedfullTemplate;
template <typename T>
struct MyNeedfullTemplate<type_matcher_t<T> >
{
};
I read answer to question partial specialization for iterator type of a specified container type that is very similar to my question, but still not sure if existing of one counter-example makes all question senseless. Also now we have brand-new c++14 and c++17 standards which could change situation. So what if I ensure the specializations is unique and exists, will than any possibility to make parameters deducible?
This is impossible, on principle, and no fancy C++9999 can change that.
What you're asking the compiler to do:
There's a use such as MyNeedfulTemplate<int> in the code. The compiler needs a definition of MyNeedfulTemplate<U> for U = int. You've tried to provide a partial specialisation of the form
template <typename T>
struct MyNeedfullTemplate<typename TypeMatcher<T>::type>
To see whether this specialisation applies or not, the compiler would have to inspect TypeMatcher<T> for all possible Ts and find if any one of them has a nested typedef type that aliases int. This cannot happen, as the set of "all possible Ts" is infinite. OK, TypeMatcher<int> doesn't have such a type, and neither does TypeMatcher<int*>, nor TypeMatcher<int**>, nor TypeMatcher<int***>. But what if TypeMatcher<int****> does? Better keep trying...
Also remember that partial and complete specialisation exists, meaning that TypeMatcher itself could be specialised.
In short, there is no way to link an int to a TypeMatcher<X>::type if all you have is the int and not the X.
You should be able to achieve something similar by re-structuring (inverting) TypeMatcher a bit:
template <class T>
struct TypeMatcher2
{
static constexpr specialised = false;
};
template <class T>
struct TypeMatcher2<std::shared_ptr<T>>
{
static constexpr specialised = true;
using OldType = T*;
};
template <class T>
struct TypeMatcher2<std::vector<T>>
{
static constexpr specialised = true;
using OldType = T&;
}
template <class T, bool spec = TypeMatcher2<T>::specialised>
struct MyNeedfullTemplate
{
// generic version
};
template <class T>
struct MyNeedfullTemplate<T, true>
{
using OriginalT = typename TypeMatcher2<T>::OldType;
// specialised version
};
I think what you're trying to do is this:
#include <iostream>
#include <memory>
#include <vector>
#include <utility>
template <typename T>
struct TypeMatcher;
template <typename T>
struct TypeMatcher<T *>
{
// making some type from T
typedef std::shared_ptr<T> type;
};
template <typename T>
struct TypeMatcher<T&>
{
// making other type from T
typedef std::vector<T> type;
};
template <typename T, typename = void>
struct MyNeedfullTemplate;
template <typename T>
struct MyNeedfullTemplate<TypeMatcher<T>, std::enable_if_t<std::is_same<typename TypeMatcher<T>::type, std::vector<std::remove_reference_t<T>>>::value>>
{
static void report() { std::cout << "hello" << std::endl; }
};
int main()
{
using matcher_type = TypeMatcher<int&>;
using full_type = MyNeedfullTemplate<matcher_type>;
full_type::report();
return 0;
}
Do I understand the question correctly?

Specialize template for types that contain typedef

I have a template class that needs to be specialized for template parameters that contain specific typedef. So I need two definitions, one for the case it has the typedef and another for the case it does not.
My problem is that I don't see how to negate the SFINAE. I could obviously eliminate the special case for the non-special parameter, but I don't know how to eliminate the default for the special parameter.
So I tried partial specialization like this:
struct NormalType { };
struct SpecialType { typedef int special; };
template <typename T, typename IsSpecial = void>
struct DetectSpecial {
void detected() { std::cout << "Not special...\n"; }
};
template <typename T>
struct DetectSpecial<T, typename T::special> {
void detected() { std::cout << "Special!\n"; }
};
but the specialization does not get used (as SSCCE on ideone).
I have also considered using enable_if, but I don't see how to use it for well-formed vs. non-well-formed expressions rather than true/false.
What is the simplest way to define DetectSpecial differently for types that contain specific typedef (the value of the typedef may be anything; it's presence is important)?
Oh, I am still stuck with some C++03 compilers. I don't think anything change in SFINAE anyway.
The minimal change required is to insert some expression in the specialization that is dependent on T::special and yields void (to match the default argument). For example:
template<class T>
struct void_alias
{
typedef void type;
};
template <typename T>
struct DetectSpecial<T, typename void_alias<typename T::special>::type> {
void detected() { std::cout << "Special!\n"; }
};
Following may help: (C++11) https://ideone.com/XISlZ6 (C++03) https://ideone.com/egKrcL
#include <cstdint>
template <typename U>
class has_special
{
private:
template<typename T> struct dummy;
template<typename T>
static std::uint8_t check(dummy<typename T::special>*);
template<typename T> static std::uint16_t check(...);
public:
static
constexpr bool value = sizeof(check<U>(0)) == sizeof(std::uint8_t);
};

Specializing a class template by a base class

I have distilled my doubt to this following piece of code
struct base {};
struct derived : public base {};
template <class T>
struct Type { };
template <> struct Type<base> {
typedef float mytype;
};
typename Type<base>::mytype a=4.2; // this works
typename Type<derived>::mytype a=4.2; // this doesnt
Could anyone explain why I cannot intantiate the class template object with derived and suggest a simple way to do it. For the actual problem that I am interested in there are many derived classes using which I want to intantiate template class objects and/or use typedefs. There are too many of them than what I would want to specialize individually.
EDIT: Forgot to mention, my bad, this needs to be C++03
#include <iostream>
#include <type_traits>
struct base { };
struct derived : base { };
template<typename T, bool = std::is_base_of<base, T>::value>
struct Type { };
template<typename T>
struct Type<T, true>
{
typedef float mytype;
};
int main()
{
Type<base>::mytype a1 = 4.2f;
Type<derived>::mytype a2 = 8.4f;
std::cout << a1 << '\n' << a2 << '\n';
}
In C++03, std can be trivially replaced with boost: boost::is_base_of
Two instantiations of a template class with different template arguments are totally unrelated class types. Type<derived> has no relation whatsoever to Type<base>, which of course means it doesn't use the specialisation and is instantiated from the primary template. The primary template has no nested type mytype.