Boost logo

Boost :

Subject: [boost] lazy_enable_if is not sfinae friendly?
From: Hui Li (hui.li_at_[hidden])
Date: 2014-04-24 19:36:01


I was surprised to find that the following code doesn't compile with boost 1.55, where boost::result_of is itself sfinae friendly.

template < typename T >
typename boost::lazy_enable_if_c<
   boost::is_class<T>::value,
   boost::result_of<T(int)>
>::type
test(int){}

template < typename >
void test(...) {}

struct A{}; // boost::result_of<A(int)> is a valid type that doesn't have a member called "type"

int main(int argc, const char * argv[])
{
   test<A>(0); // test<A>(int) meant to be a substitution failure
   return 0;
}

For example, on clang++-3.4 with -std=c++11, I got:

clang++-mp-3.4 -std=c++11 -I /opt/local/include/ main.cpp
In file included from main.cpp:1:
In file included from /opt/local/include/boost/type_traits.hpp:65:
In file included from /opt/local/include/boost/type_traits/is_nothrow_move_assignable.hpp:22:
/opt/local/include/boost/utility/enable_if.hpp:40:25: error: no type named 'type' in 'boost::result_of<A (int)>'
    typedef typename T::type type;
            ~~~~~~~~~~~~^~~~
main.cpp:12:1: note: in instantiation of template class 'boost::lazy_enable_if_c<true, boost::result_of<A (int)> >'
      requested here
typename boost::lazy_enable_if_c<
^
main.cpp:16:1: note: while substituting explicitly-specified template arguments into function template 'test'
test(int){}
^
1 error generated.

But if lazy_enable_if_c had been implemented as

template <bool B, class T>
struct lazy_enable_if_c : T {}; // inherits from T

template <class T>
struct lazy_enable_if_c<false, T> {};

The above test code would compile fine. However, the current implementation

  template <bool B, class T>
  struct lazy_enable_if_c {
    typedef typename T::type type; // this is what caused the compile error
  };

  template <class T>
  struct lazy_enable_if_c<false, T> {};

seems to evaluate T::type too soon, and results in a compile error rather than a substitution failure.

I wonder if the current implementation is chosen deliberately to disallow use cases like the test code?
But I imagine allowing lazy_enable_if etc to be (more) sfinae friendly could be very useful.

I was able to find a workaround for the test code:

template < typename T >
typename boost::enable_if_c<
   boost::is_class<T>::value,
   boost::result_of<T(int)>
>::type::type // when T = A, this is a substitution failure
test(int){}

Hui


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