std::variant<Types...>::operator=
From cppreference.com
constexpr variant& operator=(const variant& rhs); 
(1)  (since C++17) 
constexpr variant& operator=(variant&& rhs) noexcept(/* see below */); 
(2)  (since C++17) 
template <class T> variant& operator=(T&& t) noexcept(/* see below */); 
(3)  (since C++17) 
Assigns a new value to an existing variant
object.
1) Copyassignment:
 If both
*this
andrhs
are valueless by exception, does nothing.  Otherwise, if
rhs
is valueless, but*this
is not, destroys the value contained in*this
and makes it valueless.  Otherwise, if
rhs
holds the same alternative as*this
, assigns the value contained inrhs
to the value contained in*this
. If an exception is thrown,*this
does not become valueless: the value depends on the exception safety guarantee of the alternative's copy assignment.  Otherwise, if the alternative held by
rhs
is either nothrow copy constructible or not nothrow move constructible (as determined by std::is_nothrow_copy_constructible and std::is_nothrow_move_constructible, respectively), equivalent to this>emplace<rhs.index()>(get<rhs.index()>(rhs)).  Otherwise, equivalent to this>operator=(variant(rhs)). Note that
*this
may become valueless_by_exception as described in (2).
This overload is defined as deleted unless std::is_copy_constructible_v<T_i> and std::is_copy_assignable_v<T_i> are both
true
for all T_i
in Types...
. This overload is trivial if std::is_trivially_copy_constructible_v<T_i>,std::is_trivially_copy_assignable_v<T_i> and std::is_trivially_destructible_v<T_i> are all true
for all T_i
in Types...
.2) Moveassignment:
 If both
*this
andrhs
are valueless by exception, does nothing  Otherwise, if
rhs
is valueless, but*this
is not, destroys the value contained in*this
and makes it valueless  Otherwise, if
rhs
holds the same alternative as*this
, assigns std::get<j>(std::move(rhs)) to the value contained in*this
, withj
beingindex()
. If an exception is thrown,*this
does not become valueless: the value depends on the exception safety guarantee of the alternative's move assignment.  Otherwise (if
rhs
and*this
hold different alternatives), equivalent to this>emplace<rhs.index()>(get<rhs.index()>(std::move(rhs))). If an exception is thrown byT_i
's move constructor,*this
becomes valueless_by_exception.
This overload only participates in overload resolution if std::is_move_constructible_v<T_i> and std::is_move_assignable_v<T_i> are both
true
for all T_i
in Types...
. This overload is trivial if std::is_trivially_move_constructible_v<T_i>, std::is_trivially_move_assignable_v<T_i>, and std::is_trivially_destructible_v<T_i> are all true
for all T_i
in Types...
.3) Converting assignment.
 Determines the alternative type
T_j
that would be selected by overload resolution for the expression F(std::forward<T>(t)) if there was an overload of imaginary function F(T_i) for everyT_i
fromTypes...
in scope at the same time, except that:
 An overload F(T_i) is only considered if the declaration T_i x[] = { std::forward<T>(t) }; is valid for some invented variable
x
;  If
T_i
is (possibly cvqualified) bool, F(T_i) is only considered if std:remove_cvref_t<T> is also bool.
 An overload F(T_i) is only considered if the declaration T_i x[] = { std::forward<T>(t) }; is valid for some invented variable
 If
*this
already holds aT_j
, assigns std::forward<T>(t) to the value contained in*this
. If an exception is thrown,*this
does not become valueless: the value depends on the exception safety guarantee of the assignment called.  Otherwise, if std::is_nothrow_constructible_v<T_j, T>  !std::is_nothrow_move_constructible_v<T_j> is true, equivalent to this>emplace<j>(std::forward<T>(t));
 Otherwise, equivalent to this>operator=(variant(std::forward<T>(t))).
This overload only participates in overload resolution if std::decay_t<T> (until C++20)std::remove_cvref_t<T> (since C++20) is not the same type as variant and std::is_assignable_v<T_j&, T>
is true and std::is_constructible_v<T_j, T>
is true and the expression F(std::forward<T>(t)) (with F being the abovementioned set of imaginary functions) is well formed.
std::variant<string> v1; v1 = "abc"; // OK std::variant<std::string, std::string> v2; v2 = "abc"; // Error std::variant <std::string, bool> v3; v3 = "abc"; // OK, chooses string; bool is not a candidate std::variant<float, long, double> v4; //holds float v4 = 0; // OK, holds long; float and double are not candidates
Parameters
rhs    another variant

t    a value convertible to one of the variant's alternatives 
Return value
*this
Exceptions
1) May throw any exception thrown by assignment and copy/move initialization of any alternative
2)
noexcept specification:
noexcept(((std::is_nothrow_move_constructible_v<Types> && std::is_nothrow_move_assignable_v<Types>) && ...))
3)
noexcept specification:
noexcept(std::is_nothrow_assignable_v<T_j&, T> && std::is_nothrow_constructible_v<T_j, T>)
Example
This section is incomplete Reason: no example 
Defect reports
The following behaviorchanging defect reports were applied retroactively to previously published C++ standards.
DR  Applied to  Behavior as published  Correct behavior 

LWG 3024  C++17  copy assignment operator doesn't participate in overload resolution if any member type is not copyable  defined as deleted instead 
P0602R4  C++17  copy/move assignment may not be trivial even if underlying operations are trivial  required to propagate triviality 
P0608R3  C++17  converting assignment blindly assembles an overload set, leading to unintended conversions  narrowing and boolean conversions not considered 
See also
constructs a value in the variant, in place (public member function) 