A code about calling, initializing vectors and classes

Hello professionals,

I am currently reading some articles about vectors and I came across to this code:

template<typename T>
class Vec3
{
public:
// 3 most basic ways of initializing a vector
Vec3() : x(T(0)), y(T(0)), z(T(0)) {}
Vec3(const T &xx) : x(xx), y(xx), z(xx) {}
Vec3(T xx, T yy, T zz) : x(xx), y(yy), z(zz) {}
T x, y, z;

T length()
{
return sqrt(x * x + y * y + z * z);
}
};

and after reading this set of codes, another batch of codes were shown...

template<typename T>
T length(const Vec3<T> &v)
{ return sqrt(v.x * v.x + v.y * v.y + v.z * v.z); }

My question is, how can I call the class sqrt() if I will add an int main() function in my code?

For example:

int main(){

int x=3,y=3,z=1;
cout<< length(x,y,z);
system("pause");
return 0;
}

I tried this coding but it didn't work. What is the proper syntax in order to get sqrt(14)?? Your inputs will be highly appreciated. Thank you and God bless.

A link to this website where I got the codes above can also be seen here:
https://www.scratchapixel.com/lessons/mathematics-physics-for-computer-graphics/geometry/math-operations-on-points-and-vectors

std::sqrt is in the <cmath> header.
Thanks Peter. Yes, I know what you meant and I know the cmath. But I am trying to find how to call function of T length and class Vec3 properly?
You need to construct a Vec3 object and pass it to the function.

If it was a regular function you could have just wrapped the arguments in curly brackets

 
cout << length({x,y,z}); // error 

but since this is a function template the compiler doesn't know what the template argument for T should be so you'll either have to explicitly say what type you want to construct

 
cout << length(Vec3<int>{x,y,z}); // OK 

or you could pass the template argument to the function template so that the compiler knows which type is expected as argument.

 
cout << length<int>({x,y,z}); // OK 
Last edited on
Thank you Peter for your swift response. I am happy that you helped me. You are so kind.

The "length(Vec3<int>{x,y,z});" and "length<int>({x,y,z});" really worked well for the codes shown below:
template<typename T>
T length(const Vec3<T> &v)
{ return sqrt(v.x * v.x + v.y * v.y + v.z * v.z); }

Moreover, I am wondering when I tried to use the other format of this code:
template<typename T>
class Vec3
{
public:
...
// length can be a method from the class...
T length()
{
return sqrt(x * x + y * y + z * z);
}
...
};


In the code shown above, the "length(Vec3<int>{x,y,z});" and "length<int>({x,y,z});" statements are not read. Maybe I need to add another parameter for it to work?

Thank you very much for your response in advance and God bless.

If you first create an object ...

 
Vec3<int> v{3, 3, 1};

... you will then be able to call the method on that object.

 
cout << v.length();

What I have shown here is just one of many ways that you could do the same thing. If you are familiar with std::string, std::vector or other classes from the standard library you should be able to see the similarity in how they are used.


Note that I didn't really think when I picked int as the type. You probably want to use a floating point type (e.g. double) for this sort of thing.

1
2
3
4
5
Vec3<int> v{3, 3, 1};
cout << v.length() << '\n'; // prints 4

Vec3<double> u{3, 3, 1};
cout << u.length() << '\n'; // prints 4.3589 
Last edited on
Thank you very much for the swift response Peter. I tried to type your format recently but the second line says the "class Vec3<int> has no member length". I was wondering if I did something wrong with my coding. I would like to share with you my whole program...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include <iostream>
#include <cmath>


/* Callback when no events occurs */

template<typename T>
class Vec3
{
public:
	// 3 most basic ways of initializing a vector
	Vec3() : x(T(0)), y(T(0)), z(T(0)) {}
	Vec3(const T &xx) : x(xx), y(xx), z(xx) {}
	Vec3(T xx, T yy, T zz) : x(xx), y(yy), z(zz) {}
	T x, y, z;
};

//
template<typename T>
class Vec3
{
public:
	...
		// length can be a method from the class...
		T length()
	{
		return sqrt(x * x + y * y + z * z);
	}
	...
};

//
//template<typename T>
//T length(const Vec3<T> &v)
//{
//	return sqrt(v.x * v.x + v.y * v.y + v.z * v.z);
//}


int main() {
	float x=1, y=2,z=3;
	//std::cout<<length(Vec3<float>{x, y, z})<<std::endl;
	//std::cout<<length<float>({x, y, z});
	Vec3<int> v{ 3, 3, 1 };
	std::cout << v.length(); //This part shows the error saying "class Vec3<int> has no member length"
	return 0;
	
}
Last edited on
When you get a list with multiple errors you should always start with the first one.

error: redefinition of ‘class Vec3<T>’ (line 20)
error: previous definition of ‘class Vec3<T>’ (line 8)

You can't split up a class definition. The length() function should be moved inside the first definition of Vec3 (line 8-16) and the second definition of Vec3 (line 20-30) should be removed.
Wow! You are right! It is now working fine... Now, I am trying to create another functions for computing "normalize vectors" and "dot products", however, it turned out that the values for "normalize vectors" are not right... I am trying to input values a=1,b=2,c=3 and I should be able to get like:

len2 = 1 * 1 + 2* 2 + 3 * 3 = sqrt(14)

wherein,

i = 1/sqrt(14) = 0.26726
j = 2/sqrt(14) = 0.53452
k = 3/sqrt(14) = 0.80178

However, when I run my program, it shows 0 0 0 for my normalize vectors like this:

https://i.imgur.com/uWys1ZX.png

Here is my full code as of the moment...

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
//#include <GL/glut.h>
#include <iostream>
#include <cmath>
int angle = 0;
float a, b, c; 
float i, j, k;
/* Callback when no events occurs */

template<typename T>
class Vec3
{
public:
	// 3 most basic ways of initializing a vector
	Vec3() : x(T(0)), y(T(0)), z(T(0)) {}
	Vec3(const T &xx) : x(xx), y(xx), z(xx) {}
	Vec3(T xx, T yy, T zz) : x(xx), y(yy), z(zz) {}
	T x, y, z;


	T length()
	{
		return sqrt(x * x + y * y + z * z);
	}


};

//
//template<typename T>
//class Vec3
//{
//public:
//	...
//		// length can be a method from the class...
	//	T length()
	//{
	//	return sqrt(x * x + y * y + z * z);
	//}
//	...
//};

// ... or you can also compute the length in a function which is not part of the class


//
template<typename T>
T length(const Vec3<T> &v)
{
	return sqrt(v.x * v.x + v.y * v.y + v.z * v.z);
}

template<typename T>
T normalizeX(const Vec3<T> &x_)
{
	
	float len2;
	// avoid division by 0
	len2 = x_.x * x_.x + x_.y * x_.y + x_.z * x_.z;
	if (len2 > 0) {
		float invLen = 1 / sqrt(len2);
		i *= invLen, j *= invLen, k *= invLen;
	}
	return i;
}
template<typename T>
T normalizeY(const Vec3<T> &y_)
{
	
	float len2;
	// avoid division by 0
	len2 = y_.x * y_.x + y_.y * y_.y + y_.z * y_.z;
	if (len2 > 0) {
		float invLen = 1 / sqrt(len2);
		i *= invLen, j *= invLen, k *= invLen;
	}
	return j;
}
template<typename T>
T normalizeZ(const Vec3<T> &z_)
{
	
	float len2;
	// avoid division by 0
	len2 = z_.x * z_.x + z_.y * z_.y + z_.z * z_.z;
	if (len2 > 0) {
		float invLen = 1 / sqrt(len2);
		i *= invLen, j *= invLen, k *= invLen;
	}
	return k;
}

template<typename T>
T dot(const Vec3<T> &a, const Vec3<T> &b)
{
	return a.x * b.x + a.y * b.y + a.z * b.z;
}

//template<typename T>
//void normalize(Vec3<T> &v)
//{
//	T len2 = v.x * v.x + v.y * v.y + v.z * v.z;
//	// avoid division by 0
	//if (len2 > 0) {
	//	T invLen = 1 / sqrt(len2);
	//	x *= invLen, y *= invLen, z *= invLen;
	//}
//}



int main() {
	std::cout << "Enter a:";
        std::cin>> a;
	std::cout << "Enter b:";
	std::cin >> b;
	std::cout << "Enter c:";
	std::cin >> c;
	


	std::cout<<length(Vec3<float>{a, b, c})<<std::endl;
	//std::cout<<length<float>({x, y, z});
	Vec3<float> v{ a, b, c };
	std::cout << v.length();
	std::cout << "\n\nNormalizing Vectors \n";
	std::cout << normalizeX<float>({ a, b, c }); \\should return 0.26726 but showed 0.
	std::cout << " ";
	std::cout << normalizeY<float>({ a, b, c }); \\should return 0.53452 but showed 0.
	std::cout << " ";
	std::cout << normalizeZ<float>({ a, b, c }); \\should return 0.80178 but showed 0.
	std::cout << "\n";
	std::cout << "Dot Products \n";
	std::cout << dot<float>({ a, b, c }, { 1, 2, 3 });
	std::cout << "\n";
	system("pause");
	return 0;
	
}


Last edited on
At line 61, i, j, and k are global variables declared at line 6. Globals are initialized to zero so i *= Invlen just sets it to zero again and that's what gets returned.

Why do you normalize x, y, and z separately? It's probably more useful to have a method to normalize the whole vector.

Thank you @Dhayden for your feedback. I made a new coding a few minutes ago, and this time it worked, but I don't know why do I have to change the format from "i* = Invlen" to "x = v.x * invLen", which is kinda different from the pattern I see from the reference website.

Here's my new revised code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
//#include <GL/glut.h>
#include <iostream>
#include <cmath>
int angle = 0;
float a, b, c; 
float x, y, z;
/* Callback when no events occurs */

template<typename T>
class Vec3
{
public:
	// 3 most basic ways of initializing a vector
	Vec3() : x(T(0)), y(T(0)), z(T(0)) {}
	Vec3(const T &xx) : x(xx), y(xx), z(xx) {}
	Vec3(T xx, T yy, T zz) : x(xx), y(yy), z(zz) {}
	T x, y, z;


	T length()
	{
		return sqrt(x * x + y * y + z * z);
	}


};

//
//template<typename T>
//class Vec3
//{
//public:
//	...
//		// length can be a method from the class...
	//	T length()
	//{
	//	return sqrt(x * x + y * y + z * z);
	//}
//	...
//};

// ... or you can also compute the length in a function which is not part of the class


//
template<typename T>
T length(const Vec3<T> &v)
{
	return sqrt(v.x * v.x + v.y * v.y + v.z * v.z);
}

template<typename T>
T normalizeX(const Vec3<T> &v)
{

	float len2;
	// avoid division by 0
	len2 = v.x * v.x + v.y * v.y + v.z * v.z;
	//if (len2 > 0) {
	float invLen = 1 / sqrt(len2);
	x = v.x * invLen, y = v.y * invLen, z = v.z * invLen;
    //}
	return x;
}
template<typename T>
T normalizeY(const Vec3<T> &v)
{
	
	float len2;
	// avoid division by 0
	len2 = v.x * v.x + v.y * v.y + v.z * v.z;
	if (len2 > 0) {
		float invLen = 1 / sqrt(len2);
		x = v.x * invLen, y = v.y * invLen, z = v.z * invLen;
	}
	return y;
}
template<typename T>
T normalizeZ(const Vec3<T> &v)
{
	
	float len2;
	// avoid division by 0
	len2 = v.x * v.x + v.y * v.y + v.z * v.z;
	if (len2 > 0) {
		float invLen = 1 / sqrt(len2);
		x = v.x * invLen, y = v.y * invLen, z = v.z * invLen;
	}
	return z;
}

template<typename T>
T dot(const Vec3<T> &a, const Vec3<T> &b)
{
	return a.x * b.x + a.y * b.y + a.z * b.z;
}

//template<typename T>
//void normalize(Vec3<T> &v)
//{
//	T len2 = v.x * v.x + v.y * v.y + v.z * v.z;
//	// avoid division by 0
	//if (len2 > 0) {
	//	T invLen = 1 / sqrt(len2);
	//	x *= invLen, y *= invLen, z *= invLen;
	//}
//}



int main() {
	std::cout << "Enter a:";
    std::cin>> a;
	std::cout << "Enter b:";
	std::cin >> b;
	std::cout << "Enter c:";
	std::cin >> c;
	


	std::cout<<length(Vec3<float>{a, b, c})<<std::endl;
	//std::cout<<length<float>({x, y, z});
	Vec3<float> v{ a, b, c };
	std::cout << v.length();
	std::cout << "\n\nNormalizing Vectors \n";
	std::cout << normalizeX<float>({ 1, 2, 3 });
	std::cout << " ";
	std::cout << normalizeY<float>({ 1, 2, 3 });
	std::cout << " ";
	std::cout << normalizeZ<float>({ 1, 2, 3 });
	std::cout << "\n";
	std::cout << "Dot Products \n";
	std::cout << dot<float>({ a, b, c }, { 1, 2, 3 });
	std::cout << "\n";
	system("pause");
	return 0;
	
}


I actually wanted to return 3 normalized vector values like in the form of <0.267261, 0.534522, 0.801784> but my problem is I don't know how to return 3 values because I thought only 1 value can be returned in C++, that's why I planned to do it separately. I would like to know if there is a way for me to return the 3 normalized values? Thank you very much for your response in advance and God bless.
> I would like to know if there is a way for me to return the 3 normalized values?

Return a vec3<> object containing the normalised values.
1
2
template< typename T > vec3<T> normalise( const Vec3<T>& v )
{ return { normalizeX(v), normalizeY(v), normalizeZ(v) }; }

Last edited on
@JLBorges Thank you very much for your response.
I tried incorporating these codes in my program but when I tried to call it, I failed to call. Maybe there is missing with my code?

 
std::cout << Vec3<float>(normalise<float>{ a, b, c }); //I think my syntax is incorrect :/ 
Something along these lines, perhaps:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
#include <iostream>
#include <cmath>
#include <type_traits>
#include <stdexcept>

template < typename T /* some arithmetic type */ > struct vec3
{
    // sanity check: right now, we assumes that T is a built in arithmetic type
    static_assert( std::is_arithmetic<T>::value, "template argument must be an arithmetic type" ) ;

    vec3() = default ; // defaulted default constructor: initialise to zero vector
    vec3( T x, T y, T z ) : x(x), y(y), z(z) {}
    // the other foundation operations (copy/move/assignment/destructor) are implicitly defaulted

    // TO DO: declare a defaulted copy constructor vec3( const vec3<T>& that ) = default ;
    //        and add a templated copy constructor to facilitate copy initialising
    //        vec3<double> from an object of type vec3<int> etc.

    bool is_zero() const { return x == 0 && y == 0 &&  z == 0 ; }

    // return: float if T == float, long double if T == long double, double otherwise
    // note: unqualified look up of sqrt (because in future we may want to extend this to use
    //       user/library defined extended precision numeric types. eg. boost::multiprecision::cpp_bin_float)
    auto magnitude() const { using std::sqrt ; return sqrt( x*x + y*y + z*z ) ; }

    // return vec3<long double> if T == long double, vec3<double> otherwise
    vec3< std::common_type_t<T,double> > normalise() const
    {
        if( is_zero() ) throw std::domain_error( "zero vector!" ) ;

        const auto m = magnitude() ; // check if m is zero / near zero?
        return { x/m, y/m, z/m } ;
    }

    T x = 0 ; // default member initialisers
    T y = 0 ;
    T z = 0 ;

    friend std::ostream& operator << ( std::ostream& stm, vec3<T> v )
    { return stm << "( " << v.x << ", " << v.y << ", " << v.z << " )" ; }

    // syntactic sugar: many people might prefer the constructs
    // magnitude_of(v) over v.magnitude() and normalised(v) over v.normalise()
    friend auto magnitude_of( vec3<T> v ) { return v.magnitude() ; }
    friend auto normalised( vec3<T> v ) { return v.normalise() ; }
};

int main()
{
    const vec3<int> vi( 1, 2, 3 ) ; // note: in C++17, we could just write: const vec3 vi( 1, 2, 3 ) ;
                                    //             (class template argument deduction)

    const auto vid = normalised(vi) ; // vec3<double> holding the normalised values of vec3<int>
    std::cout << vi << "  magnitude: "  << magnitude_of(vi) << "  normalised: " << vid << '\n' ;

    vec3<double> vd( vi.x, vi.y, vi.z ) ; // construct vec3<double> from parts of vec3<int>
    const auto vdn = normalised(vi) ;
    std::cout << std::showpoint << vd << "  normalised: " << vdn << '\n' ;

    try // attempt to normalise a zero vector
    {
        vd = {} ; // make it a zero vector (by assigning a default constructed vector)
        std::cout << normalised(vd) << '\n' ; // error: this throws
    }
    catch( const std::exception& e )
    {
        std::cout << "*** error: " << e.what() << '\n' ;
    }
}

http://coliru.stacked-crooked.com/a/61a3f1bd51067946
http://rextester.com/GYSUL92012
Wow, I am really overwhelmed how these calling of classes/functions are diversified in terms of it forms. I am honestly and literally new to these "calling" techniques and still having a hard time digesting all of them (which is probably the hindrance why I am having a hard time understanding and making codes).

I would like to ask how do you manage to understand all of these patterns? Is it because of doing trial and error in the coding and see if it works fine, then keep on typing again typing in the C++ until it works fine? @JLBorges

Apologies again for my questions... still a novice in programming. :)
> Wow, I am really overwhelmed how these calling of classes/functions are diversified in terms of it forms.

It is not as complicated as it may appear at first sight. Let us say, we have this simple class:
1
2
3
4
5
6
struct vec3
{
    double x = 0 ;
    double y = 0 ;
    double z = 0 ;
};

Right now, all it has are data members x, y and z.
We can create an object of type vec3 and access its data members this way:
1
2
3
4
5
6
7
8
int main()
{
    vec3 v ;
    
    std::cout << v.x << '\n' ; // print the data member x of he object 
                               // this prints 0 because x is initalised with zero
                               // v.x => the data member x of the object v  
}


The full program: http://coliru.stacked-crooked.com/a/59b9464ec451f060

Now, let us add a member function to the class:
1
2
3
4
5
6
7
8
struct vec3
{
    double x = 0 ;
    double y = 0 ;
    double z = 0 ;
    
    double magnitude() const ; // *** added: member function
};

The class now has four members: three data members (aka member variables) and one member function.

This member function looks quite similar to a normal (non-member) function with some important differences:

a. The 'full' name of this member function is vec3::magnitude (rather than just plain magnitude).

b. This function operates on an object of type vec3; we can call it on an object of type vec3.
Just as in our earlier program, v.x accesses the data member x of the object v,
now v.magnitude() would call the member function magnitude on the object v. (When calling the member function, we don't have to use its full name; that magnitude() must a member of v is obvious.)

c. From within the function, we can access the data members of the object (on which it was called) directly.
In main, we referred to v.x; within the member function we can just refer to x.

The definition of the function:
double vec3::magnitude() const { return std::sqrt( x*x + y*y + z*z ) ; }
Here, x, y and z refer to the x, y and z data members of the object on which he function was called.
(The const at the end is a promise that this function will not modify any of the data members of the object.)

Putting it all together:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <iostream>
#include <cmath>

struct vec3
{
    double x = 0 ;
    double y = 0 ;
    double z = 0 ;

    double magnitude() const ; // *** added: member function
};

double vec3::magnitude() const { return std::sqrt( x*x + y*y + z*z ) ; }

int main()
{
    vec3 v ;

    // give some non zero values to the data members x and y
    v.x = 3 ;
    v.y = 4 ;

    const double m = v.magnitude() ; // v.magnitude() : call the member function magnitude on the object v
                                     // store the result of the call in the variable m

    std::cout << std::showpoint << m << '\n' ; // print out the value of m
}

http://coliru.stacked-crooked.com/a/59b9464ec451f060


Essentially, that is all that there is; that is all we need to know to get started.
Thank you very much @JLBorges, @Peter87, @dhayden for always sharing your helping hands to novice individuals like me. God bless you all. Thank you @JLBorges for sharing these links. I think I have to practice a lot more to know more of these codes better.

And yes, I will post my queries in this wonderful website always so I can also help for other beginners like me. God bless you. :)
Topic archived. No new replies allowed.