1

I have two types defined in a header file like so:

struct vec2{
    float x;
    float y;
    vec2() : x(0), y(0) {}
};
struct vec3{
    float x;
    float y;
    float z;
    vec3() : x(0), y(0), z(0) {}
};

In some C++ file I would like to be able to write:

vec2 a2 = {2,4};
vec3 a3 = vec2(a2);
//This would give me a3 with a z-value of 0.

Which would work by adding this to the header:

 vec3(vec2 a) : x(a.x), y(a.y), z(0) {}

But I would also like to be able to write:

vec3 a3 = {1,2,4};
vec2 a2 = vec3(a3); //Where this would result in a3's z component being dropped.

Is this possible? Would I need to typedef it somehow so the compiler knows ahead of time what size the 2 structures are?

Infant
  • 13
  • 3
  • Just use a forward declaration for which ever struct is declared second. – mdf Aug 03 '22 at 20:16
  • why do you use `vec2 a2 = vec3(a3);` instead of `vec2 a2 = a3;`? The effect should be the same. Arguably you should be making these conversion constructors `explicit` to adoid accidentally converting, but this would result in `vec2 a2 = vec2(a3);` or `vec2 a2(a3);` being the appropriate syntax. – fabian Aug 03 '22 at 20:29
  • @fabian: That does make more sense indeed. – Infant Aug 03 '22 at 20:58

2 Answers2

0

You can do something like:

// Forward-declaration
struct vec2;
struct vec3;

struct vec2{
    float x;
    float y;
    vec2() : x(0), y(0) {}
    vec2(const vec3 &v3) : x(v3.x), y(v3.y) {}
};

struct vec3{
    float x;
    float y;
    float z;
    vec3() : x(0), y(0), z(0) {}
    vec3(const vec2 &v2) : x(v2.x), y(v2.y), z(0) {}
};

Note: This should also be separated into a header and source file (see Separating class code into a header and cpp file)

mdf
  • 133
  • 8
  • Not sure about the separation to header+cpp file: For simple logic like this it may be a good idea to allow for more compiler optimizations by keeping the functionality inline... – fabian Aug 03 '22 at 20:25
0

No problem, you have to set the constructors up to perform implicit conversions:

namespace vec{ // if in a header, this should avoid possible name conflicts
    struct vec3;   // forward declaration

    struct vec2{
        float x;
        float y;
        vec2(float inp1 = 0, float inp2 = 0): 
            x(inp1), y(inp2) {}
        vec2(const vec3& inp);   // vec3 forward declared
    };

    struct vec3{
        float x;
        float y;
        float z;
        vec3(float inp1 = 0, float inp2 = 0, float inp3 = 0): 
            x(inp1), y(inp2), z(inp3) {}
        vec3(const vec2& inp): 
            x(inp.x), y(inp.y), z(0) {}
    };

    vec2::vec2(const vec3& inp): 
        x(inp.x), y(inp.y) {}
}

Test function:

int main()
{
    using namespace vec;
    vec3 a = {1,2,3};
    vec2 a_minus(a);
    vec3 a_again(a_minus);
    
    std::cout << a_minus.x << ", " << a_minus.y << ", " << a_again.z <<'\n';

    return 0;
}

Output:

1, 2, 0
Giogre
  • 1,444
  • 7
  • 19
  • It should be noted that this code snippet being part of a header file requires marking `vec2::vec2(const vec3&)` as `inline`, at least if you want to include the header in more than one translation unit. Furthermore `inp1`, ect. as names for the constructor parameters is something I'd avoid, since it won't work well with code completion helpers: This could easily be improved by using e.g. `xVal` or `x_` or a similar name instead. – fabian Aug 03 '22 at 20:34
  • I think the `inline` keyword is most useful to inline data members in headers. Member functions are inlined automatically by modern compilers. Or, better phrased, compilers are able to evaluate which member function is apt to be inlined better than a programmer could do. – Giogre Aug 03 '22 at 20:39
  • 1
    Any C++11 compliant compiler must treat functions ***defined in the class body*** as inline, but it must not treat function definitions outside of the class body as inline. `vec2::vec2(const vec3& inp)` is not defined in the class body and therefore is not inline which does not necessarily prevent compiler optimizations, but it will result in a ODR violation, if the header is included in more than one translation unit. – fabian Aug 03 '22 at 20:47
  • 1
    It is magical! I will look into seeing what the compiler does differently with the inline keyword. – Infant Aug 03 '22 at 20:50
  • Compiler optimizations can take care of that. The way I see it, the issue that you correctly note can rise with multiple translation units, can also be resolved by enclosing the header content inside a `namespace`. – Giogre Aug 03 '22 at 20:52