11

Let's say I have some templated class depending on type T. T could be almost anything: int, int*, pair <int, int> or struct lol; it cannot be void, a reference or anything cv-qualified though. For some optimization I need to know if I can subclass T. So, I'd need some trait type is_subclassable, determined as a logical combination of basic traits or through some SFINAE tricks.

In the original example, int and int* are not subclassable, while pair <int, int> and struct lol are.

EDIT: As litb pointed out below, unions are also not subclassable and T can be a union type as well.

How do I write the trait type I need?

1 Answers1

13

You want to determine whether it is a non-union class. There is no way known to me to do that (and boost hasn't found a way either). If you can live with union cases false positives, you can use a is_class.

template<typename> struct void_ { typedef void type; };

template<typename T, typename = void>
struct is_class { static bool const value = false; };

template<typename T>
struct is_class<T, typename void_<int T::*>::type> { 
  static bool const value = true; 
};

Boost has an is_union that uses compiler-specific builtins though, which will help you here. is_class (which boost also provides) combined with is_union will solve your problem.

Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
  • I forgot the union case completely. Unfortunately no, false positives are not acceptable. Misses are, since this is an optimization after all. –  Jun 30 '11 at 20:56
  • @doublep then unless you are using c++0x, I don't think it's solvable in a portable way. – Johannes Schaub - litb Jun 30 '11 at 20:59
  • Yeah, I guess I'll have to make the optimization conditional on C++0x support or just drop it then. –  Jun 30 '11 at 21:02
  • 1
    `is_class` doesn't resolve to true for unions, unless gcc is wrong here: http://ideone.com/P1J6d – Benjamin Lindley Jun 30 '11 at 21:04
  • Another non-subclassable case is enums it seems. But yeah, also not classes (even `enum class`). –  Jun 30 '11 at 21:05
  • @Benjamin, mine does. C++0x and boost's apparently don't. – Johannes Schaub - litb Jun 30 '11 at 21:11
  • 1
    @litb: A later standard draft says about `is_class`: "`T` is a class type but not a union type (3.9.2)". –  Jun 30 '11 at 21:16
  • @Johannes: From your report, "This means that `is_class` shall be derived from `true_type` if `T` is an union" - according to the standard, no `§20.9.4.1 [meta.unary.cat]`: "`template struct is_class;` - T is a class type but not a union type.", unless I misread your report. Also, `p3` same paragraph: "[ Note: For any given type T, exactly one of the primary type categories has a value member that evaluates to true. —end note ]" – Xeo Jun 30 '11 at 21:28
  • @Xeo boost does not implement the Standard. Its documentation specifies what is being implemented. It says nothing about unions being excluded, and points to 3.9.2 and 9.2 of the C++ Standard. – Johannes Schaub - litb Jun 30 '11 at 21:35
  • @Johannes: Erm, right, forget my comment. :P I need to change my view on Boost as part of the standard... – Xeo Jun 30 '11 at 21:36
  • I have started a thread(http://stackoverflow.com/questions/6543652/different-template-syntax-for-finding-if-argument-is-a-class-or-not) to understand your syntax. One thing, I cannot understand still is that why `void_::type` and the `typename = void` needs to be same (here `void`) ? – iammilind Jul 01 '11 at 14:15
  • @iammilind because a partial specialization only matches if the types match the template arguments. That's the whole point about partial and explicit specializations. I could have written `typename = char[1]` and in the specialization I could have written `char[sizeof (int T::*), 1]`. Whatever I would write, the types need to match for the specialization to be chosen, of course. – Johannes Schaub - litb Jul 01 '11 at 14:28