Boost logo

Boost :

Subject: Re: [boost] enable_if and non-template member functions?
From: Mostafa (mostafa_working_away_at_[hidden])
Date: 2011-08-12 19:42:25


On Fri, 12 Aug 2011 14:14:24 -0700, Jeremiah Willcock
<jewillco_at_[hidden]> wrote:

> On Fri, 12 Aug 2011, Mostafa wrote:
>
>> On Fri, 12 Aug 2011 02:31:10 -0700, John Maddock
>> <boost.regex_at_[hidden]> wrote:
>>
>>>> From your description, I think I've done exactly this. E.g.
>>> --8<---------------cut here---------------start------------->8---
>>> template <class U>
>>> struct X
>>> {
>>> // member function void f(int) becomes something like:
>>> template <class T>
>>> typename enable_if<
>>> mpl::and_<
>>> is_convertible<T,int>
>>> , some_condition_on<U>
>>> >
>>> >::type
>>> f(T x_)
>>> { int x = x_; ... }
>>> };
>>> --8<---------------cut here---------------end--------------->8---
>>> That works as long as the function has at least one parameter that
>>> can be turned into a template, but I have some cases that are
>>> operators or else have no parameters.
>>
>> In case of no-parameter functions or operators, what's wrong with:
>>
>> template <class U>
>> struct T
>> {
>> void f()
>> {
>> f_impl(<static_cast<void *>(0));
>> }
>>
>> private:
>> template <class T>
>> typename enable_if<
>> mpl::and_<
>> is_convertible<T, void *>,
>> some_condition_on<U>
>> >::type
>> f_impl(T)
>> { .... }
>> };
>
> In that case, f would not be covered by SFINAE, since it is not a
> template. Calls to f would fail because of the lack of f_impl,

Yes, I'm proposing moving the SFINAE failure one level deep to point where
f_impl is called.

> but that would be done after overload resolution.

I'm presuming you're talking about choosing between different multiple
return types for no-parameter non-template member functions. For example,
i) if template-parameter U is a class, we would want: CLASS * operator*();
ii) else: void * operator*();

In such a scenario wouldn't one just use a template-selector struct on the
return type? The following example illustrates both scenarios, and it
works on 4.0.1,

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

using namespace boost;

struct CLASS {};

template <class U>
struct T
{
   void f()
   {
       this->f_impl(static_cast<void *>(0));
   }

   void bar()
   {
       std::cout << "Non-class template parameter instantiations work." <<
std::endl;
   }

   struct deref
   {
       template <class R, class ENABLER = void> struct ret;
       template <class R>
       struct ret<R, typename enable_if<is_class<R> >::type >
       { typedef U * type; };
       template <class R>
       struct ret<R, typename enable_if<mpl::not_<is_class<R> > >::type >
       { typedef void * type; };
   };
   typename deref::template ret<U>::type operator*()
   {
       return this->deref_impl(static_cast<void *>(0));
   }

private:
   template <class T>
   typename enable_if<
       mpl::and_<
           is_same<T, void *>,
           is_class<U>
>
>::type
    f_impl(T)
    {
       std::cout << "Class template parameter instantiations work." <<
std::endl;
    }

   template <class T>
   typename enable_if<
       mpl::and_<
           is_same<T, void *>,
           is_class<U>
>,
       /*U **/typename deref::template ret<U>::type
>::type
    deref_impl(T)
    {
       std::cout << "U * ret type." << std::endl;
       return 0;
    }

   template <class T>
   typename enable_if<
       mpl::and_<
           is_same<T, void *>,
           mpl::not_<is_class<U> >
>,
       /*void **/typename deref::template ret<U>::type
>::type
    deref_impl(T)
    {
       std::cout << "void * ret type." << std::endl;
       return 0;
    }
};

int main()
{
     T<CLASS> ins;
     ins.f();
     *ins;

     T<int> ins2;
     ins2.bar();
     *ins2;
// ins2.f(); //Expected compiler error.

     return 0;
}

- Mostafa


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