Ronnie Shipman
Ronnie Shipman

Reputation: 31

Why can't std::variant types be classes with an explicit copy constructor?

If I create a std::variant using classes for the types, and add an explicit copy constructor to one of those classes, the compiler complains. With the default or a non-explicit copy constructor it compiles fine.

Example:

#include <iostream>
#include <cstdint>
#include <variant>

class A
{
    int m_a{0};
public:
    A() = default;
    A(int a) : m_a{a}
    {
        std::cout << "construct a" << std::endl;
    }
};

class B
{
    int m_b{0};
public:
    B() = default;
    B(int b) : m_b{b}
    {
        std::cout << "construct b" << std::endl;
    }

    //B(const B& other)             // <-- Does compile
    explicit B(const B& other)      // <-- Does not compile
    {
        m_b = other.m_b;
    }
};

class C
{
    int m_c{0};
public:
    C() = default;
    C(int c) : m_c{c}
    {
        std::cout << "construct c" << std::endl;
    }
};

int main()
{
    std::variant<A, B, C> v;

    B b{123};
    v = b;

    return 0;
}

MSVC, gcc and clang all generate the equivalent error. Can anyone explain why this is please? Is it a deliberate restriction?

Upvotes: 1

Views: 309

Answers (1)

LoS
LoS

Reputation: 1833

The Standard specifies that, to participate in overload resolution, the operator=(T&& t) signature must satisfy the following constraints:

  • the cvref-unqualified type T must not be the same as the current std::variant specialization;
  • the alternative type Tj must be assignable from the type T;
  • the alternative type Tj must be copy constructible from the type T;
  • the expression FUN(std::forward<T>(t)) must be well-formed.

The issue is essentially the last condition. Indeed, the FUN() function must perform a trick to find the alternative type Tj, which is B in your case. In particular, the operation is the following: built an imaginary function FUN(Tj) for each alternative type Tj for which Tj x[] = {std::forward<T>(t)} is well-formed for some invented variable x, the overload FUN(Tj) selected by overload resolution, for the expression FUN(std::forward<T>(t)), defines the alternative type Tj, which is the type of the contained value after assignment.

Since the B class does not provide an implicit copy constructor, the above expression is never well-formed. The consequence is that the function is not considered in overload resolution, forcing the compiler to look for other functions and, since they are not suitable to perform the intended operation, a compile-time error raises.

Upvotes: 0

Related Questions