24

Does the following code invoke undefined behaviour?

std::variant<A,B> v = ...;

std::visit([&v](auto& e){
  if constexpr (std::is_same_v<std::remove_reference_t<decltype(e)>,A>)
    e.some_modifying_operation_on_A();
  else {
    int i = e.some_accessor_of_B();
    v = some_function_returning_A(i); 
  }
}, v);

In particular, when the variant does not contain an A, this code re-assigns an A while still holding a reference to the previously held object of type B. However, because the reference is not used anymore after the assignment, I feel the code is fine. However, would a standard-library be free to implement std::visit in a way such that the above is undefined behaviour?

Barry
  • 286,269
  • 29
  • 621
  • 977
burnpanck
  • 1,955
  • 1
  • 12
  • 36
  • 5
    Do you want quotes from the standard to back up the answer(s) you get? – NathanOliver Mar 15 '19 at 17:08
  • 1
    From looking at [\[variant.visit\]](http://eel.is/c++draft/variant.visit), I'm 99% sure this code is compliant and guaranteed not to have UB, since `std::visit(vis, variant)` should be equivalent to `vis(get* active member */>(variant))`, but I'm not confident enough in reading the standard to be certain – Justin Mar 15 '19 at 17:27
  • @NathanOliver: I don't need actual quotes from the standard, as long as the experts here can agree on the answer:-). – burnpanck Mar 15 '19 at 17:31

1 Answers1

18

The code is fine.

There is no requirement in the specification of std::visit that the visitor not change the alternative of any of the variants it is invoked on. The only requirement is:

Requires: For each valid pack m, e(m) shall be a valid expression. All such expressions shall be of the same type and value category; otherwise, the program is ill-formed.

Your visitor is a valid expression for each m and always returns void, so it satisfies the requirements and has well-defined behavior.

Barry
  • 286,269
  • 29
  • 621
  • 977