enable_if and non-template member functions?

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? Thanks, John.

On Sat, Aug 6, 2011 at 1:38 AM, John Maddock <boost.regex@virgin.net> 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?
One thing I've done in this situation is the following. Let's say we want to provide 2 different definitions of a member function f within class X<T>, depending on if T satisfies some metafunction cond. Then you can do something like: template< class T > struct X { struct dummy_t { }; typedef /*...*/ f_arg_type; // f's argument type typedef typename boost::mpl::if_< cond<T>, f_arg_type, dummy_t >::type cond_true_f_arg_type; typedef typename boost::mpl::if_< cond<T>, dummy_t, f_arg_type >::type cond_false_f_arg_type; void f(cond_true_f_arg_type) { /*...may assume T does satisfy cond...*/ } void f(cond_false_f_arg_type) { /*...may assume T does not satisfy cond...*/ } }; I think this is fine as long as you don't declare explicit instantiations of X (at least, I don't recall ever having any problems with this technique). Is that kind of what you're looking for? - Jeff

On 08/06/2011 10:38 AM, John Maddock 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?
Make a member function that forwards to a free template function?

On Sat, Aug 6, 2011 at 12:43 PM, Mathias Gaunard <mathias.gaunard@ens-lyon.org> wrote:
On 08/06/2011 10:38 AM, John Maddock 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?
Make a member function that forwards to a free template function?
Or maybe class specializations?

On Sat, Aug 6, 2011 at 4:38 AM, John Maddock <boost.regex@virgin.net> 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?
Thanks, John.
C++0x has a simple answer to this -- default function template template arguments. This works with constructors and even conversion operators. I added documentation for this kind of usage to trunk back in April or so, but I guess it never made it into Release. A quick example of what you can do in 0x: /////////////////////////// class foo { typedef double value_type; template< class A = int , typename enable_if_c < sizeof( A ) != 0 && your_arbitrary_condition< value_type >::value , int >::type = 0 > foo() { /* some constructor enabled only if value_type meets the condition */ } }; /////////////////////////// There are two sort of subtle things here. First, the sizeof( A ) != 0 part is to force the condition to be dependent on a template argument. The other subtle thing is that the second template argument is a non-type template argument. It was suggested that a macro should be made to hide the subtleties and make things more concise. In this situation it seems like it would simplify things a lot. I'll put something together and make sure it all gets to Release. -- -Matt Calabrese

On Sat, Aug 6, 2011 at 3:40 PM, Matt Calabrese <rivorus@gmail.com> wrote:
I'll put something together and make sure it all gets to Release.
Opinions on the following macro implementation? http://codepaste.net/svq4hu I could make an _T version for C++0x compilers that support the required 0x features other than the relaxed typename rule, but I'm not sure if that's necessary or if such compilers exist (right now I just always use typename to eliminate the need for more macros). I'll also do some basic error reporting if the macro is invoked when it is not supported. -- -Matt Calabrese

On 08/06/2011 10:29 PM, Matt Calabrese wrote:
On Sat, Aug 6, 2011 at 3:40 PM, Matt Calabrese<rivorus@gmail.com> wrote:
I'll put something together and make sure it all gets to Release.
Opinions on the following macro implementation? http://codepaste.net/svq4hu
You managed to cram so many non-C++03 things in that macro it's impressive. __COUNTER__, variadic macros, decltype and function template default args. I think that for those reasons, it's pretty much unusable in real code.

On Sat, Aug 6, 2011 at 5:54 PM, Mathias Gaunard < mathias.gaunard@ens-lyon.org> wrote:
On 08/06/2011 10:29 PM, Matt Calabrese wrote:
On Sat, Aug 6, 2011 at 3:40 PM, Matt Calabrese<rivorus@gmail.com> wrote:
I'll put something together and make sure it all gets to Release.
Opinions on the following macro implementation? http://codepaste.net/svq4hu
You managed to cram so many non-C++03 things in that macro it's impressive.
__COUNTER__, variadic macros, decltype and function template default args.
I think that for those reasons, it's pretty much unusable in real code.
I'm not sure why that rules it out for "real code". It's true that you need a couple of 0x features, but that's because it's the only way for such a macro to exist. So either it uses 0x or there is no macro at all. That said, on the two compilers I've tested that support default template arguments to function templates, GCC and Clang, it works fine, so I don't really see the issue. The decltype trick is the only thing not strictly necessary and it does imply a couple of extra template instantiations, so I could always remove it. That part is just there to make it so that the single macro can be used with either bool constants or types (so there's no need for a separate _C version of the macro). Everything else is either essential for the macro to exist in any form or shouldn't impact compile times. -- -Matt Calabrese

On Sat, 6 Aug 2011, Matt Calabrese wrote:
On Sat, Aug 6, 2011 at 4:38 AM, John Maddock <boost.regex@virgin.net> 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?
Thanks, John.
C++0x has a simple answer to this -- default function template template arguments. This works with constructors and even conversion operators. I added documentation for this kind of usage to trunk back in April or so, but I guess it never made it into Release. A quick example of what you can do in 0x:
Sorry about that -- I probably forgot to check enable_if when I was looking for things to merge. Is the release branch open yet? If so, I'll copy it over ASAP. -- Jeremiah Willcock

On Sun, Aug 7, 2011 at 4:31 PM, Jeremiah Willcock <jewillco@osl.iu.edu>wrote:
Sorry about that -- I probably forgot to check enable_if when I was looking for things to merge. Is the release branch open yet? If so, I'll copy it over ASAP.
Great, not a problem. Do you have any opinions on the macro? The documented solution is long, somewhat unintuitive, and very easy to mess up in subtle ways, so a macro probably should be there in some form, as was suggested in the other thread. I think Thorsten also put one together as well. If you agree that a macro is useful, I wouldn't mind adjusting implementation if necessary and making some quick tests. -- -Matt Calabrese

On Mon, 8 Aug 2011, Matt Calabrese wrote:
On Sun, Aug 7, 2011 at 4:31 PM, Jeremiah Willcock <jewillco@osl.iu.edu>wrote:
Sorry about that -- I probably forgot to check enable_if when I was looking for things to merge. Is the release branch open yet? If so, I'll copy it over ASAP.
Great, not a problem. Do you have any opinions on the macro? The documented solution is long, somewhat unintuitive, and very easy to mess up in subtle ways, so a macro probably should be there in some form, as was suggested in the other thread. I think Thorsten also put one together as well. If you agree that a macro is useful, I wouldn't mind adjusting implementation if necessary and making some quick tests.
I think the macro would be useful, but looking at the implementation, I'm not sure it works with types like you claim. Also, what is the code on lines 45-46 doing? I don't see how that would ever be used by the current definitions of your macros. -- Jeremiah Willcock

On Mon, Aug 8, 2011 at 8:55 AM, Jeremiah Willcock <jewillco@osl.iu.edu>wrote:
I think the macro would be useful, but looking at the implementation, I'm not sure it works with types like you claim. Also, what is the code on lines 45-46 doing? I don't see how that would ever be used by the current definitions of your macros.
It does, try it out. I'll comment the trick a little better or remove it if people have a problem with it for some reason (it implies a couple of template instantiations so that's a legitimate criticism, though I don't think use of the macro would be common enough for it to have a negligible impact on compile-times). The idea is that I want the single macro to be usable with values or types (mpl integral constants), so I need to be able to have the macro expand to a bit of C++ that implicitly converts the bool constant to an mpl integral constant and leaves the mpl constant as is, or the other way around. The trick I came up with to accomplish that is this: ////////// // Make two function template overloads, here both called "foo": // One that takes a bool constant // The return type is the mpl integral constant equivalent of the bool template< bool Value > mpl::bool_< Value > foo(); // And one that takes an mpl integral constant // The return type is just the argument being passed template< class Type > Type foo(); // Now if we do: // decltype( foo< something_goes_here >() ) // We will always get an mpl integral constant representing the argument // This works if something_goes_here is a bool constant or if it's an mpl constant // For example: // Yields mpl::bool_< true > typedef decltype( foo< true >() ) condition1; // Yields boost::is_void< void > typedef decltype( foo< boost::is_void< void > >() ) condition2; ////////// Understand? So by using function template overloads and decltype you can convert the bool to an mpl integral constant without the user having to be aware. If an mpl integral constant is passed, then it is left alone. The actual trick in the code is slightly more complex and that is because I also handle the potentially common case of a bool condition with a greater than sign in it such as: sizeof( something ) > sizeof( void* ) This issue here is that the > wouldn't actually be a greater than operator at all after macro expansion, it would instead end the function template argument list that appears inside the decltype. A user who doesn't realize this would get a weird error, which could ultimately be resolved by just wrapping their expression in parentheses, but that isn't the easiest thing to notice for most programmers. To make it so that a user doesn't have to know to do that, I just wrap the argument in parentheses from the inside of the macro. But now, if I wrap all macro arguments internally in parentheses, I have the problem that a parenthesized type is not valid on its own! To get around that, what I do is I append "bool" in front of the parentheses. If you don't immediately see what that get's you, check it out: ////////// // In the case of a value, we get a compile-time cast to bool. No problem. bool( sizeof( something ) > sizeof( void* ) ) // In the case of a type we get a function type that // returns bool // takes our condition as a parameter type bool( is_void< void > ) ////////// Now that I have a bool or a function type with the condition in the first parameter, I need to rewrite the original overloads: ////////// // This overload stays the same // The return type is the mpl integral constant equivalent of the bool template< bool Value > mpl::bool_< Value > foo(); // This overload is updated to pull out the parameter type instead of the type directly // The return type is the parameter type of the function template< class Type > typename function_traits< Type >::arg1_type foo(); ////////// Just to be complete, here's what the decltype part of the macro looks like with everything update after macro expansion (it obviously expands to more than just this, though here is the interesting part -- the function is also not called "foo" in the actual implementation, it's called "enable_if_condition_getter"): ////////// // BOOST_ENABLE_IF( true ) // Internally yields mpl::bool_< true > from the decltype trick // decltype( foo< bool(true) >() ) // // BOOST_ENABLE_IF( boost::is_void< void > ) // Internally yields boost::is_void< void > from the decltype trick // decltype( foo< bool(boost::is_void< void >) >() ) ////////// So now there can exist a single macro that works with mpl integral constants or bools, even if they have a top-level >, without the user of the macro having to know anything about the implementation. Anyway, if people really hate this, it can be removed and replaced by BOOST_ENABLE_IF and BOOST_ENABLE_IF_C which each have their own separate implementation -- one for types and one for bools. I just prefer to provide the simplest interface possible with the least subtleties for the user, even if it complicates the code a bit behind the scenes. If the template instantiations are perceived to be a problem, then two macros instead of one is a reasonable option. -- -Matt Calabrese

On 8/8/2011 1:44 PM, Matt Calabrese wrote:
On Mon, Aug 8, 2011 at 8:55 AM, Jeremiah Willcock<jewillco@osl.iu.edu>wrote:
I think the macro would be useful, but looking at the implementation, I'm not sure it works with types like you claim. Also, what is the code on lines 45-46 doing? I don't see how that would ever be used by the current definitions of your macros.
It does, try it out. I'll comment the trick a little better or remove it if people have a problem with it for some reason (it implies a couple of template instantiations so that's a legitimate criticism, though I don't think use of the macro would be common enough for it to have a negligible impact on compile-times). The idea is that I want the single macro to be usable with values or types (mpl integral constants), so I need to be able to have the macro expand to a bit of C++ that implicitly converts the bool constant to an mpl integral constant and leaves the mpl constant as is, or the other way around. The trick I came up with to accomplish that is this:
////////// // Make two function template overloads, here both called "foo":
// One that takes a bool constant // The return type is the mpl integral constant equivalent of the bool template< bool Value> mpl::bool_< Value> foo();
// And one that takes an mpl integral constant // The return type is just the argument being passed template< class Type> Type foo();
// Now if we do: // decltype( foo< something_goes_here>() ) // We will always get an mpl integral constant representing the argument // This works if something_goes_here is a bool constant or if it's an mpl constant
// For example: // Yields mpl::bool_< true> typedef decltype( foo< true>() ) condition1;
// Yields boost::is_void< void> typedef decltype( foo< boost::is_void< void> >() ) condition2; //////////
Understand? So by using function template overloads and decltype you can convert the bool to an mpl integral constant without the user having to be aware. If an mpl integral constant is passed, then it is left alone.
The actual trick in the code is slightly more complex and that is because I also handle the potentially common case of a bool condition with a greater than sign in it such as: sizeof( something )> sizeof( void* )
This issue here is that the> wouldn't actually be a greater than operator at all after macro expansion, it would instead end the function template argument list that appears inside the decltype. A user who doesn't realize this would get a weird error, which could ultimately be resolved by just wrapping their expression in parentheses, but that isn't the easiest thing to notice for most programmers. To make it so that a user doesn't have to know to do that, I just wrap the argument in parentheses from the inside of the macro. But now, if I wrap all macro arguments internally in parentheses, I have the problem that a parenthesized type is not valid on its own! To get around that, what I do is I append "bool" in front of the parentheses. If you don't immediately see what that get's you, check it out:
////////// // In the case of a value, we get a compile-time cast to bool. No problem. bool( sizeof( something )> sizeof( void* ) )
// In the case of a type we get a function type that // returns bool // takes our condition as a parameter type bool( is_void< void> ) //////////
Now that I have a bool or a function type with the condition in the first parameter, I need to rewrite the original overloads:
////////// // This overload stays the same // The return type is the mpl integral constant equivalent of the bool template< bool Value> mpl::bool_< Value> foo();
// This overload is updated to pull out the parameter type instead of the type directly // The return type is the parameter type of the function template< class Type> typename function_traits< Type>::arg1_type foo(); //////////
Just to be complete, here's what the decltype part of the macro looks like with everything update after macro expansion (it obviously expands to more than just this, though here is the interesting part -- the function is also not called "foo" in the actual implementation, it's called "enable_if_condition_getter"):
////////// // BOOST_ENABLE_IF( true ) // Internally yields mpl::bool_< true> from the decltype trick // decltype( foo< bool(true)>() ) // // BOOST_ENABLE_IF( boost::is_void< void> ) // Internally yields boost::is_void< void> from the decltype trick // decltype( foo< bool(boost::is_void< void>)>() ) //////////
So now there can exist a single macro that works with mpl integral constants or bools, even if they have a top-level>, without the user of the macro having to know anything about the implementation. Anyway, if people really hate this, it can be removed and replaced by BOOST_ENABLE_IF and BOOST_ENABLE_IF_C which each have their own separate implementation -- one for types and one for bools. I just prefer to provide the simplest interface possible with the least subtleties for the user, even if it complicates the code a bit behind the scenes. If the template instantiations are perceived to be a problem, then two macros instead of one is a reasonable option.
You are absolutely right. It is important that the end user has the easiest interface to do what is necessary whenever possible. The implementation can always be designed well, and have enough comments in the code, that another developer can understand it rather than being dumbed down in any way because it may be difficult to understand.

on Sat Aug 06 2011, Matt Calabrese <rivorus-AT-gmail.com> wrote:
C++0x has a simple answer to this -- default function template template arguments.
<snip>
There are two sort of subtle things here. First, the sizeof( A ) != 0 part is to force the condition to be dependent on a template argument. The other subtle thing is that the second template argument is a non-type template argument.
And this you're calling "simple?"
It was suggested that a macro should be made to hide the subtleties and make things more concise. In this situation it seems like it would simplify things a lot. I'll put something together and make sure it all gets to Release.
+1 -- Dave Abrahams BoostPro Computing http://www.boostpro.com

On Sat, Aug 6, 2011 at 1:38 AM, John Maddock <boost.regex@virgin.net> 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?
I'm afraid what I'm suggesting is obvious and you probably thought of it already but you could convert your design to use namespace-scope function templates instead of member functions of a class template. If you insist on the member function call syntax, you could add member functions that forward to the (enable-iffed) namespace-scope functions. Emil Dotchevski Reverge Studios, Inc. http://www.revergestudios.com/reblog/index.php?n=ReCode

On 6 August 2011 09:38, John Maddock <boost.regex@virgin.net> 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); }

on Sun Aug 07 2011, Daniel James <dnljms-AT-gmail.com> wrote:
On 6 August 2011 09:38, John Maddock <boost.regex@virgin.net> 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.
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--- -- Dave Abrahams BoostPro Computing http://www.boostpro.com

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. So for now I think I'll stick to the old "this member can't be instantiated unless T models X". It prevents explicit instantiation, but never mind I guess... BTW, it would be great if all these techniques could be added to the enable_if docs. Cheers, John.

On Fri, 12 Aug 2011 02:31:10 -0700, John Maddock <boost.regex@virgin.net> 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) { .... } };
BTW, it would be great if all these techniques could be added to the enable_if docs.
+1 It would be great if all the docs had a best-practices section and a commonly-encountered-techniques section. -Mostafa

On Fri, 12 Aug 2011, Mostafa wrote:
On Fri, 12 Aug 2011 02:31:10 -0700, John Maddock <boost.regex@virgin.net> 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, but that would be done after overload resolution. -- Jeremiah Willcock

On Fri, 12 Aug 2011 14:14:24 -0700, Jeremiah Willcock <jewillco@osl.iu.edu> wrote:
On Fri, 12 Aug 2011, Mostafa wrote:
On Fri, 12 Aug 2011 02:31:10 -0700, John Maddock <boost.regex@virgin.net> 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

In case of no-parameter functions or operators, what's wrong with:
Frankly it's easier and cleaner to just put a static assert in the function body - the effect is the same - a compiler error if the operation isn't supported - and you at least get an error message that doesn't make it look like your code is broken ;-) John.
participants (11)
-
Daniel James
-
Dave Abrahams
-
Edward Diener
-
Emil Dotchevski
-
Jeffrey Lee Hellrung, Jr.
-
Jeremiah Willcock
-
John Maddock
-
Mathias Gaunard
-
Matt Calabrese
-
Mostafa
-
Thomas Heller