1

I've hit a wall concerning this explicit copy constructor issue. I've been writing a class to figure things out:

#include <iostream>

template<class T>
class Mat
{
private:
    T data;
public:
    void set(T value)
    {
        data = value;
    }

    Mat()
        : data(T(0))
    {
    }

    explicit Mat(const Mat& another)
    {
        *this = another;
    }

    Mat& operator=(const Mat& another)
    {
        data = another.data;
        return *this;
    }

    template<class U>
    explicit operator Mat<U>()
    {
        Mat<U> result;
        result.set(static_cast<U>(data));
        return result;
    }

    void print()
    {
        std::cout << data << std::endl;
    }
};


int main()
{
    Mat< double > d1;
    d1.set(3.14159);
    Mat< int > i1(static_cast<Mat<int>>(d1));
    d1.print();
    i1.print();

    std::cin.sync();
    std::cin.ignore();
    return 0;
}

I want my copy constructor to take only explicitly converted instances of another object, so I declared it explicit, but now I get the error error "C2558: class 'Mat' : no copy constructor available or copy constructor is declared 'explicit'", even though I made an explicit cast:

static_cast<Mat<int>>(d1)

I've declared the copy constructor explicit because I want this to be illegal:

Mat<float> a;
Mat<int>   b(a);

While, I would like the following to remain legal:

Mat<float> a;
Mat<int>   b(static_cast<Mat<int>>(a));

EDIT: I've been tinkering with this concepts trying to define exactly what I want to get, and I seem to get some funny results:

#include <iostream>

class MatB
{
private:
    float data;
public:
    MatB()
        :data(0.0f)
    {

    }

    void set(float value)
    {
        data = value;
    }

    float getData() const
    {
        return data;
    }

    void print()
    {
        std::cout << data << std::endl;
    }
};

class MatA
{
private:
    double data;
public:


    MatA()
        :data(0.0)
    {

    }

    void set(double value)
    {
        data = value;
    }

    double getData() const
    {
        return data;
    }

    explicit operator MatB()
    {
        MatB temp;
        temp.set(static_cast<float>(getData()));
        return temp;
    }

    void print()
    {
        std::cout << data << std::endl;
    }
};



class MatC
{
private: 
    int data;
public:

    MatC()
        :data(0)
    {

    }

    explicit MatC(const MatB& in)
        :data(static_cast<int>(in.getData()))
    {

    }

    void print()
    {
        std::cout << data << std::endl;
    }
};

int main()
{
    MatA someA;
    someA.set(3.14159);
    MatC constructCFromA(someA);
    someA.print();
    constructCFromA.print();

    std::cin.sync();
    std::cin.ignore();
    return 0;
}

In this example, constructCFromA(someA) shouldn't compile (imo) - even the linker marks it as an error(VS2013), still it compiles just fine... I am not sure whether my understanding of 'explicit' is incorrect, whether the IDE marks it as an error incorrectly, or the compiler compiles it even though it shouldn't. I thought I would need to do something like this:

constructCFromA(static_cast<MatB>(someA));

The IDE seems to agree with me, but the compiler doesn't. I must say I am pretty confused.

EDIT2: Never mind, in Ideone it doesn't compile, so I guess MS are to blame. I think the 2nd code illustrates well the behaviour I want to get. Basically make non-explicit conversions at initialization and assignment illegal. It seems however, that making the copy constructor explicit has various "side-effects".

lightxbulb
  • 1,251
  • 12
  • 29
  • 1
    You cannot static cast `Mat< double >` to `Mat< int >` in first place, these class types are unrelated. – πάντα ῥεῖ Dec 26 '14 at 13:49
  • That's not an implicit conversion, since you are explicitly constructing your `Mat`. – Quentin Dec 26 '14 at 13:49
  • If I remove the explicit before the copy ctor, it works just fine. Why can't I cast Mat to Mat, when I've provided a cast for that? – lightxbulb Dec 26 '14 at 13:52
  • 2
    Well, I never yet tried making the copy-ctor `explicit`. Always seemed a bad (and useless?) idea... – Deduplicator Dec 26 '14 at 13:53
  • 2
    it breaks my heart to see a redundant default construction of T in the copy constructor, and to see a copy construction written in terms of an assignment. Oh the humanity... :( – Richard Hodges Dec 26 '14 at 13:56
  • How would I go about not repeating(and reusing) my code without using the assignment operator in the copy ctor? I'll be more than happy to know of an elegant solution. – lightxbulb Dec 26 '14 at 14:02
  • 1
    @lightxbulb See [copy-and-swap](http://stackoverflow.com/questions/3279543/what-is-the-copy-and-swap-idiom). Basically, you'd do it the other way around: put the main code in the copy constructor, and use that (indirectly) from the assignment operator. The benefits of that are described in the answers there. –  Dec 26 '14 at 15:19

2 Answers2

4

The line where you did the explicit cast is not a problem. The problem that causes the compiling issue is on the line where you return Mat<U> by value:

template<class U>
explicit operator Mat<U>()
{
    Mat<U> result;
    result.set(static_cast<U>(data));
    return result;  // <<== This line requires a copy constructor to be defined
}

That is why when you remove explicit before the copy ctor, your code works just fine.

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • The method expects a Mat return value, why would it need a copy ctor when I am returning a value of type Mat? How would I go about restricting implicit type conversion in the copy ctor? – lightxbulb Dec 26 '14 at 13:59
  • 3
    @lightxbulb In the absence of return value optimization returning an object by value invokes copy constructor. Even when return value optimization is performed, the compiler must pay attention to having access to an implicit copy constructor. That's why you need a copy constructor when returning `Mat`. – Sergey Kalinichenko Dec 26 '14 at 14:09
  • So from what I get, the issue is that my copy constructor, being explicit, cannot be used in the scenario you pointed out? How would I force explicit type conversions in the copy ctor then? – lightxbulb Dec 26 '14 at 14:18
  • 2
    @lightxbulb I am not clear on what behavior you would like to avoid by declaring the copy constructor explicit. Could you please edit the question with a line that shows a code fragment that you want to prevent from compiling, in addition to another fragment that you want to remain compilable? – Sergey Kalinichenko Dec 26 '14 at 14:45
  • 2
    @lightxbulb I see what you are trying to do. Prohibiting `Mat b(a);` from compiling and keeping the conversion operator at the same time would be problematic, because C++ considers `b(a)` explicit enough to call your conversion operator. – Sergey Kalinichenko Dec 26 '14 at 15:11
2

here's another version that will pass your unit test without the need for a conversion operator to T:

    template<class T>
    class Mat
    {
    private:
        T data;
    public:
        void set(T value)
        {
            data = value;
        }

        // default constructor
        Mat()
        : data(T(0))
        {
        }

        // construct from data type
        explicit Mat(T dat)
        : data(dat)
        {}

        // construct from any compatible Mat
        template<class U>
        explicit Mat(const Mat<U>& another)
        : data(static_cast<T>(another.get_data()))
        {}

        // assign from any compatible Mat
        template<class U>
        Mat& operator=(const Mat<U>& another)
        {
            data = static_cast<T>(another.get_data());
            return *this;
        }

        // provide a means to access data from unrelated Mat
        const T& get_data() const { return data; }

        void print()
        {
            std::cout << data << std::endl;
        }
    };
Richard Hodges
  • 68,278
  • 7
  • 90
  • 142
  • The method constructing from any compatible Mat is not considered a copy ctor by C++ standards though, right? http://stackoverflow.com/questions/19167201/copy-constructor-of-template-class So is there a way to making it work with an actual copy ctor? – lightxbulb Dec 26 '14 at 14:21