1

I have a class defined as:

template <typename V, typename E> class AdjacencyList; 

Where V and E are the types of the vertex and edge values respectively.

I am currently attempting to define the following member function inside AdjacencyList:

std::map< std::shared_ptr< Vertex<V, E> >, E > dijkstra( const std::shared_ptr< Vertex<V, E> > &start_vertex) const; 

For those familiar with Dijkstra's algorithm, it is only possible to implement correctly if E is an addable and non-negative type. Therefore, how do I correctly use the enable_if construct to only enable this function if E is an unsigned integral type?

I am currently seeing two complications here that I am uncomfortable with approaching:

  1. Both the return type and the parameter concern E.
  2. E itself is not used as a type, but is used in other type templates.

Because I am relatively new to the enable_if construct, I would feel more comfortable with some guidance on the issue since this is a relatively non-trivial case.

3 Answers 3

1

This is not actually what you want to do.

The point of std::enable_if is to cause substitution failure for a template. The reason why you want substitution failure is because it's not a failure, and you can select a different overload instead. However, there's no point here because you're not trying to choose a different overload, you're just trying to make it a failure.

So, you'd instead do something like this:

std::map< std::shared_ptr< Vertex<V, E> >, E > dijkstra( const std::shared_ptr< Vertex<V, E> > &start_vertex) const { static_assert( std::is_unsigned<E>::value, "E must be unsigned."); } 

If you try to call this function with the wrong type of arguments, you get a nice compile-time error telling you that E must be addable and non-negative. If you used enable_if instead, you'd get an error that none of the overloads were valid, which is a less informative error.

This is probably a bad design, though. Ordinarily you would just throw an exception if you encountered a negative value. Choosing to enforce that the inputs are positive is also incomplete, since the algorithm will also fail if you encounter overflow.

(If you really want to do enable_if even though it's a bad idea, you can..)

std::enable_if<std::is_unsigned<E>, std::map<std::shared_ptr<Vertex<V, E>>, E>::type dijkstra... 

If you actually want to program this way

C++ is the wrong language for this type of programming, and hammering C++ until it works this way will result in some very bizzare C++ code. It sounds like you actually want to write code in Agda.

Sign up to request clarification or add additional context in comments.

2 Comments

Thank you. This helped me to better understand SFINAE and SFIAAE. This problem found myself in a situation in which I was trying to carry over the traits systems found in languages such as Swift and Rust. I believe C++ would have greatly benefitted from something like this, although the time for implementing it has long passed.
@JaredPayne: The static_assert is basically equivalent to the Rust or Swift constraints.
1

enable_if is helpful if you want to write several overloads with adjusted type requirements. In your case you don't have any overloads and only want to enforce some type restrictions. So you can just use static_assert to check them and give user a meaningful diagnostic message instead of usual template instantiation failure mess. Something like this:

<type_traits> <utility> ... static_assert(::std::is_unsigned< E >::value, "edge value type must be unsigned integral type"); static_assert(::std::is_same< E, decltype(::std::declval< E >() + ::std::declval< E >()) >, "edge value type must be addable"); 

Comments

1

only enable this function if E is an unsigned integral type

I get your comment/request as it is and use directly the tools provided by <type_traits>.
If you want to use std::enable_if, probably the following works almost fine for you:

std::enable_if_t<std::is_integral<E>::value and std::is_unsigned<E>::value, std::map< std::shared_ptr< Vertex<V, E> >, E >> dijkstra( const std::shared_ptr< Vertex<V, E> > &start_vertex) const; 

Anyway, SFINAE (Substitution Failure Is Not An Error) makes sense when you have more than one option, otherwise you are doing something we can call Substitution Failure Is Always An Error. In these cases, a static_assert is more appropriate:

static_assert(std::is_integral<E>::value and std::is_unsigned<E>::value, "!"); 

Put it as the very first line of your function. Error messages with static asserts are also usually more user friendly.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.