Search code examples
c++stltemplate-meta-programming

Can enable_if be used as a non-extra parameter (e.g. for a constructor)?


I am writing a new container, and trying to comply with N3485 23.2.3 [sequence.reqmts]/14, which states:

For every sequence container defined in this Clause and in Clause 21:

  • If the constructor

    template <class InputIterator>
    X(InputIterator first, InputIterator last, 
         const allocator_type& alloc = allocator_type())
    

    is called with a type InputIterator that does not qualify as an input iterator, then the constructor shall not participate in overload resolution.

(/14 repeats this almost verbatim for the member functions taking iterator ranges)

N3485 23.2.3 [sequence.reqmts]/15 says:

The extent to which an implementation determines that a type cannot be an input iterator is unspecified, except that as a minimum integral types shall not qualify as input iterators.

My understanding is that the phrase "shall not participate in overload resolution" means that the container implementer is supposed to use SFINAE tricks to disable that constructor or member function during template argument deduction. For the member functions, this is no big deal; as the return type on a function is the normal way to use enable_if. But for the constructor, there is no return type on which enable_if can be applied. This was my first attempt to declare the constructor:

// The enable_if use below is to comply with 23.2.3 [sequence.reqmts]/14:
//     ... is called with a type InputIterator that does not qualify as an input iterator
//     then the constructor shall not participate in overload resolution.
template <typename InputIterator>
path(std::enable_if<!std::is_integral<InputIterator>::value, InputIterator>::type first,
     InputIterator last, Allocator const& allocator = allocator_type());

However, boost's enable_if docs suggest using a dummy pointer parameter initialized to nullptr instead of using an actual parameter to a function. Is that necessary for correct behavior here or is the preceding declaration of path's iterator range constructor okay?


Solution

  • The Good Robot (R. Martinho Fernandes) discusses the issue with a clean C++11 solution, namely using a default template parameter to apply enable_if, in his blog.

    However, let me just point out here that doing

        template< class Type >
        void foo( typename Something< Type >::T )
        
    

    foils argument deduction.

    It's still possible to call the function, by explicitly providing the template argument. But in C++ the compiler will simply refuse to match e.g. a MyType actual argument to formal argument type Something<Blah>::T, because while this can be done in some special cases it cannot always be done (there could be countless choices of Blah where Something<Blah>::T was MyType).

    So, your current approach won't work in general, but the whole problem of the specification's requirement is a C++11 problem, so the C++11 specific solution is OK! :-)