Boost logo

Boost :

Subject: Re: [boost] enable_if and non-template member functions?
From: Daniel James (dnljms_at_[hidden])
Date: 2011-08-07 03:52:03


On 6 August 2011 09:38, John Maddock <boost.regex_at_[hidden]> wrote:
> Folks, I have a problem with enable_if:  it works just dandy for template
> member functions in my class, but I have some *non-template* member
> functions and constructors that I want to enable only if the template is
> instantiated with template args that meet certain criteria.  Seems like I
> can't do this with enable_if as it then generates an invalid class
> definition.  I guess I could use base classes, and/or partial
> specialization, but both involve a lot of code duplication I'd rather not
> have.  Anyone any ideas?

If I understand you correctly, something along the lines of the
following would work. You turn a non-template parameter into a
template and devise some type trait to check it (I'm sure you can do a
better job than me for the traits class). It's a bit verbose, but if
you're using this enough it shouldn't be too hard to write a class to
improve things a little.

I'm not sure if there's any way to do this for no parameters. And it
might be worth putting the implementation into another template
function, and then casting before calling it (to avoid generating lots
of versions of the function for slight variations in how it's called).

I don't think I'd use this technique unless I really needed to though.
Maybe there's a better way.

#include <boost/utility/enable_if.hpp>
#include <boost/type_traits/is_same.hpp>
#include <boost/mpl/and.hpp>
#include <iostream>

// Type trait to check that the parameter is appropriate.

template <typename From, typename To>
struct call_for
{
    struct type
    {
        static char check_call(To);
        static long check_call(...);

        enum { value =
            sizeof(check_call(*(From*)0)) == sizeof(char)
        };
    };
};

// Some structs for testing making calls.

struct convert_from {};

struct implicit_conversion {
    implicit_conversion() {}
    implicit_conversion(convert_from const&) {}
};

struct explicit_conversion {
    explicit_conversion() {}
    explicit explicit_conversion(convert_from const&) {}
};

template <typename T>
struct test_class
{
    // void foo(implicit_conversion) if boost::is_same<T, char*>
    template <typename T1>
    void foo(
        T1 x,
        typename boost::enable_if<
            boost::mpl::and_<
                call_for<T1, implicit_conversion>,
                boost::is_same<T, char*>
>,
            void*
>::type = 0)
    {
        std::cout << "char*" << std::endl;
    }

    // void foo(implicit_conversion) if boost::is_same<T, long>
    template <typename T1>
    void foo(
        T1 x,
        typename boost::enable_if<
            boost::mpl::and_<
                call_for<T1, implicit_conversion>,
                boost::is_same<T, long>
>,
            void*
>::type = 0)
    {
        std::cout << "long" << std::endl;
    }

    // void foo(explicit_conversion) if boost::is_same<T, int*>
    template <typename T1>
    void foo(
        T1 x,
        typename boost::enable_if<
            boost::mpl::and_<
                call_for<T1, explicit_conversion>,
                boost::is_same<T, int*>
>,
            void*
>::type = 0)
    {
        std::cout << "int*" << std::endl;
    }
};

int main()
{
    test_class<char*> x1;
    test_class<long> x2;
    test_class<int*> x3;

    convert_from a1;
    implicit_conversion a2;
    explicit_conversion a3;

    // Commented out lines are (correct) errors.
    x1.foo(a1);
    x1.foo(a2);
    //x1.foo(a3);

    x2.foo(a1);
    x2.foo(a2);
    //x2.foo(a3);

    //x3.foo(a1);
    //x3.foo(a2);
    x3.foo(a3);
}


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk