3

I need a template expression that selects the first argument type if the first argument is defined, else the second argument type.

select<int, float>::type               // type evaluates to int
select<an_undefined_type, float>::type // type evaluates to float

... and the solution has to work with C++03 and Boost 1.33.1 :(

My goal is to accept both int and boost::optional<int> as a function template parameter T, so I can do something like:

template<typename T>
void fn(T& t)
{
    int x = std::numeric_limits<select<T::value_type, T>::type>::digits;
}

since boost::optional<int>::value_type is defined.

C++11 solutions are also appreciated.

I don't see a way to do this with template specialization, since I'm trying to specialize not on types but on concepts. Basically, I would need one specialization that matches the any_integer concept and one specialization that matches the boost::optional<any_integer> concept.

I guess with C++11 I could accomplish this specific goal with:

std::conditional<std::is_integral<T>::value, T, T::value_type>::value

but I don't have C++11, and I want the more general solution.

James Brock
  • 3,236
  • 1
  • 28
  • 33
  • Wouldn't accepting just optional be sufficient? Also, C++11 is already year old; not having it is weird, unless you are developing for some very specific architecture which doesn't have updated/maintained compiler. – Griwes Nov 10 '12 at 13:08
  • I'm looking at this which may have the answer: http://stackoverflow.com/questions/11813940/possible-to-use-type-traits-sfinae-to-find-if-a-class-defines-a-member-type – James Brock Nov 10 '12 at 13:10
  • I think this is the answer: http://stackoverflow.com/a/11816999/187223 – James Brock Nov 10 '12 at 13:12
  • 1
    @Griwes: I am afraid you live in a Unicorn World. At the company I work at we have been transitioning from gcc 3.4.2 to gcc 4.3.2 for a couple *years* now. And as long as *that* transition does not end, we are not going to be moving on to another version. C++11 is years ahead for us... and from what I gathered on the web, I might be blessed, in that gcc 4.3.2 *is not so old*. – Matthieu M. Nov 10 '12 at 13:17
  • @MatthieuM., that's (i.e., human stupidity) probably the reason I'm currently wanting to end up working at an uni, not in some company using God-forsaken versions of compilers... Still, the OP didn't answer the more important part of my initial comment. – Griwes Nov 10 '12 at 13:21
  • @Griwes: It's not only human stupidity. It's a matter of cost. We have a couple *thousands* of libraries to migrate; each time a library is migrated there are chances for bugs or performances regressions to sneak in, and risks (and tests) cost. Therefore managers may prefer a different focus (delivering immediate values to clients). Of course, as developers, it may be painful. But providing 24/7 quality services to our clients, and being able to quickly adapt to their changing needs (as the market itself changes), is what pays in the end. – Matthieu M. Nov 10 '12 at 13:55

1 Answers1

2

I don't think you can achieve the exact notation you are looking for. However, I think you can use a slightly different notation to achieve what you are semantically after. The problem with your current notation

int x = std::numeric_limits<select<T::value_type, T>::type>::digits;

is that select<T0, T1> expects two types, i.e., the requirement for the types to be present isn't on the select<T0, T1> template but on the function calling it. The way I would change this is to use

int x = std::numeric_limits<select<typename get_value_type<T>::type, T>::type>::digits;

Now all what needs to happen is to have a get_value_type<T> which yields the nested type if present and some type select<T0, T1> is going to ignore if arrives there, e.g., void (or a custom marker type). The get_value_type<T> template should be fairly simple (I saw Dirk Holsopple's answer but I couldn't get it work):

template <typename T>
struct has_value_type
{
    typedef char (&true_type)[1];
    typedef char (&false_type)[2];
    template <typename D> static true_type test(typename D::value_type*);
    template <typename D> static false_type test(...);

    enum { value = sizeof(test<T>(0)) == 1 };
};

template <typename T, bool = has_value_type<T>::value >
struct get_value_type
{
    typedef T type; // EDIT
};

template <typename T>
struct get_value_type<T, true>
{
    typedef typename T::value_type type;
};

Obviously, you might want to define your type-traits slightly different so you can use something like

int x = std::numeric_limits<typename get_type<T>::type>::digits;

This would return the nested type if there is a value_type and a type T otherwise.

Community
  • 1
  • 1
Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
  • Answer accepted, thanks Dietmar! (There is one typo on line 15, `typedef void type` should be `typedef T type`) – James Brock Nov 10 '12 at 15:31
  • Also edited usage example, previously was `int x = std::numeric_limits – James Brock Nov 12 '12 at 01:32
  • The usage example used to say what I describe above the example. Below the example I pointed out that it isn't really necessary depending on what you want to do with it: Since your immediate application is more specific than what you originally asked for, it can be simplified using a more specific solution. – Dietmar Kühl Nov 12 '12 at 01:35
  • Oh I see, I've reverted the usage example. Also, thanks for pointing out "the requirement for the types to be present isn't on the template but on the function calling it." – James Brock Nov 12 '12 at 05:42