views:

131

answers:

4

I want to enforce explicit conversion between structs kind of like native types:

int i1;
i1 = some_float; // this generates a warning
i1 = int(some_float): // this is OK
int i3 = some_float; // this generates a warning

I thought to use an assignment operator and copy constructor to do the same thing, but the behavior is different:

Struct s1;
s1 = other_struct; // this calls the assignment operator which generates my warning
s1 = Struct(other_struct) // this calls the copy constructor to generate a new Struct and then passes that new instance to s1's assignment operator
Struct s3 = other_struct; // this calls the COPY CONSTRUCTOR and succeeds with no warning

Are there any tricks to get that third case Struct s3 = other_struct; construct s3 with the default constructor and then call the assignment operator?

This all compiles and runs as it should. The default behavior of C++ is to call the copy constructor instead of the assignment operator when you create a new instance and call the copy constructor at once, (i.e. MyStruct s = other_struct;becomes MyStruct s(other_struct); not MyStruct s; s = other_struct;. I'm just wondering if there are any tricks to get around that.

EDIT: The "explicit" keyword is just what I needed!

class foo {
    foo(const foo& f) { ... }
    explicit foo(const bar& b) { ... }
    foo& operator =(const foo& f) { ... }
};

foo f;
bar b;
foo f2 = f; // this works
foo f3 = b; // this doesn't, thanks to the explicit keyword!
foo f4 = foo(b); // this works - you're forced to do an "explicit conversion"
+2  A: 

In short, no.

The long version...actually that's about it. That's just not how it works. Had to come up with something to fill the character requirement though.

Noah Roberts
Possible long version: `MyStruct s(other_struct)` is no more nor less obviously a conversion than `MyStruct s = other_struct`. In both cases you state the type of `s`, right there on that line of code, but not the type of `other_struct`. So why should they be any different from the POV of explicit/implicit conversion? And indeed, they aren't.
Steve Jessop
A: 

I don't think so. When you write

Struct s3 = other_struct;

It looks like an assignment, but really it's just declarative syntax that calls a constructor.

Reinderien
+3  A: 

You can get around this if you overload the type cast operator for other_struct, and edit the original structure accordingly. That said, it's extremely messy and there generally isn't a good reason to do so.


#include <iostream>

using namespace std;

struct bar;

struct foo {
    explicit foo() {
        cout << "In foo default constructor." << endl;
    }

    explicit foo(bar const &) {
        cout << "In foo 'bar' contructor." << endl;
    }

    foo(foo const &) {
        cout << "In foo constructor." << endl;
    }

    foo const & operator=(bar const &) {
        cout << "In foo = operator." << endl;
        return *this;
    }
};

struct bar {
    operator foo() {
        cout << "In bar cast overload." << endl;
        foo x;
        x = *this;
        return x;
    }
};

int main() {
    bar b;
    foo f = b;
    return 0;
}

Outputs:

In bar cast overload.
In foo default constructor.
In foo = operator.
In foo constructor.
In foo constructor.
Paul
Called the copy constructor twice.
Noah Roberts
@Noah: I believe that's because the compiler can't optimize away the copy created from the cast operator when it returns.
Paul
Perhaps. Point is that it fails to provide the OP with what they want. Sure, it does add a call to the assignment operator in there but it does not change the essential nature of Type t = x. The variable is still constructed with the copy constructor. All you've done is insert a temporary in there that happens to be created with the default + op=.
Noah Roberts
@Noah: It never constructs it with the "bar" copy constructor - just the foo one. It copies it from bar with the assignment operator.
Chris
@Noah: As Chris said above, it does not touch the "bar" copy constructor. The OP wanted to avoid this by first using the default constructor and then the assignment operator. It does exactly what the OP requested.
Paul
+2  A: 

Disclaimer: I'm ready to take the downvotes on this, since this doesn't answer the question. But this could be useful to the OP.

I think it is a very bad idea to think of the copy constructor as default construction + assignment. It is the other way around:

struct some_struct
{
    some_struct();  // If you want a default constructor, fine
    some_struct(some_struct const&); // Implement it in the most natural way
    some_struct(foo const&);         // Implement it in the most natural way

    void swap(some_struct&) throw(); // Implement it in the most efficient way

    // Google "copy and swap idiom" for this one
    some_struct& operator=(some_struct x) { x.swap(*this); return *this; }

    // Same idea
    some_struct& operator=(foo const& x)
    {
        some_struct tmp(x);
        tmp.swap(*this);
        return *this;
    }
};

Implementing things that way is fool proof, and is the best you can obtain in term of conversion semantics in C++, so it is the way to go here.

Alexandre C.