Use Boost enable_if to handle ambiguous function overload return types

The title is not really clear but I didn't found a better one. The example will be clearer (I hope). In EDDI, I had this little function :

template<typename Visitor, typename Visitable>
void visit(Visitor&amp; visitor, Visitable&amp; visitable){
    visitor(visitable);
}

For the record, this function is only invoking a specific operator of a visitor. The problem was that I wanted this function to handle also non-void visitors. The visitor in question has a result_type typedef indicating the return type of the visit. The naive version cannot work :

template<typename Visitor, typename Visitable>
typename Visitor::result_type visit(Visitor&amp; visitor, Visitable&amp; visitable){
    return visitor(visitable);
}

template<typename Visitor, typename Visitable>
void visit(Visitor&amp; visitor, Visitable&amp; visitable){
    visitor(visitable);
}

The problem here is that there are ambiguities for overload resolution because the return type is not considered in this resolution. What we want is that the overload resolution does not consider the function returning something (the first one). And that's here that Boost can help, specifically the Boost enable_if library. This function allows to enable of disable some function template of function class based on a boolean condition. In our case, we want to disable the function is the return type is void. So, we will use the boost::disable_if template to disable it. This template has to parameter B and T. When B is true, the template is evaluated to T, otherwise there is an error used for SFINAE (Substitution failure is not an error) To test if the return type is void, we will use Boost type_traits, specifically the boost::is_void template.

Here is the version using disable_if :

template<typename Visitor, typename Visitable>
typename boost::disable_if<boost::is_void<typename Visitor::result_type>, typename Visitor::result_type>::type
visit(Visitor&amp; visitor, Visitable&amp; visitable){
    return visitor(visitable);
}

template<typename Visitor, typename Visitable>
void visit(Visitor&amp; visitor, Visitable&amp; visitable){
    visitor(visitable);
}

With that, you can call the visitor with a void return type. However, it's not enough. Indeed, the call is still ambiguous when the return type is not void. So we have to enable the second function only if the return type is void :

template<typename Visitor, typename Visitable>
typename boost::disable_if<boost::is_void<typename Visitor::result_type>, typename Visitor::result_type>::type
visit(Visitor&amp; visitor, Visitable&amp; visitable){
    return visitor(visitable);
}

template<typename Visitor, typename Visitable>
typename boost::enable_if<boost::is_void<typename Visitor::result_type>, typename Visitor::result_type>::type
visit(Visitor&amp; visitor, Visitable&amp; visitable){
    visitor(visitable);
}

With that, you can call the function with both visitors and the good function will be chosen depending on the result type of the visitor.

I hope this example of using Boost enable_if will help you when you face similar problems.

Related articles

  • Boost 1.48.0 has been released
  • Manage command-line options with Boost Program Options
  • Simulate static_if with C++11/C++14
  • eddic 0.5.1 : Better assembly generation and faster parsing
  • eddic 1.2.4: New Boost Spirit X3 parser and minor cleanups
  • Run your Boost Tests in parallel with CMake
  • Comments

    Comments powered by Disqus