MarcusAseth

Questions about Templates

Recommended Posts

I was doing an experiment, code below:

template<typename T>
struct S
{
	explicit S(T v) :val{ v } {};
	T val;
};

int main()
{
	S<int> MyS('g');
	cout << MyS.val << endl;//Output is 103

	return 0;
}

Even though I am providing T with a type which is int, and I have an explicit constructor, why I have an implicit conversion from char to int? Shouldn't it see that T is int since I'm telling it so and thus give me error for constructing an S out of char? :S

 

Edited by MarcusAseth

Share this post


Link to post
Share on other sites

The explicit keyword means that it won't create an instance of the object without you explicitly constructing one - for example, as part of an expression where it might normally make sense to the compiler to cast some object to this type, but where you want to disallow this.

Here, you do explicitly construct an object of the type, so it calls the constructor, and it makes the normal effort to convert the argument for you.

In this example, it's the 'int' that has an 'implicit' or 'converting' constructor, which magically creates an int out of a char. You don't get to change the semantics of ints, though.

To recap: explicit constructors don't say anything about their arguments, they say things about when they can be legitimately called.

Share this post


Link to post
Share on other sites

I'm changing the title of the topic into "Questions about Templates" because I'm experimenting with it and I know I will have more than one, doesn't seems right to make 1000 mini-topics :/

So now I have the code below:

template<typename T>
struct S
{
	S(T v) :val{ v } {};
	T val;
};

template <typename R, typename T>
R& get(T& e)
{
	return e.val;
}

int main()
{
	S<char> MySChar('B');
	cout << "MySChar: " << MySChar.val << endl;
	
	cout << "Returned val: " << get<char>(MySChar) << endl;

	return 0;
}

The function get is not part of the S class, as required from the book exercise (which is "" Add a function template get() that returns a reference to val.")

I don't like that I have to specify the return type when I call it, if I have 5 different instantiation of S it becomes messy, so I was trying to make it in such a way that I can just write get(MySChar) and it just works with wathever instantiation of S, but I am failing on it, code below:

template<typename T>
struct S
{
	S(T v) :val{ v } {};
	T val;
	typedef T type;
};

template <typename T, typename R>
R& get(T& e)
{
	using R = e.type;
	return e.val;
}

int main()
{
	S<char> MySChar('B');

	cout << "MySChar: " << MySChar.val << endl;

	cout << "Returned val: " << get(MySChar) << endl;

	return 0;
}

How would I do that the proper way? :S

 

Share this post


Link to post
Share on other sites
47 minutes ago, MarcusAseth said:

I was doing an experiment, code below:


template<typename T>
struct S
{
	explicit S(T v) :val{ v } {};
	T val;
};

int main()
{
	S<int> MyS('g');
	cout << MyS.val << endl;//Output is 103

	return 0;
}

Even though I am providing T with a type which is int, and I have an explicit constructor, why I have an implicit conversion from char to int? Shouldn't it see that T is int since I'm telling it so and thus give me error for constructing an S out of char?

 

 

In C++ words, this is called promotion. Promotions happen automatically.

See for example Integral promotion of this page.

Share this post


Link to post
Share on other sites

Well, I kind of found a solution to my last question (by throwing everything at random at it), which is auto& return type. But still out of curiosity, what would be the second best alternative to auto, using templates? :P

template <typename T>
auto& get(T& e)
{
	return e.val;
}

 

Edited by MarcusAseth

Share this post


Link to post
Share on other sites
15 minutes ago, Kylotan said:

but this is all too abstract for me to even work out what you're trying to do at a glance.

nothing too fancy, I'm just trying to make the code below compile without changing it but just by modifying that get() function 

The code below the commented line that says "//THIS 5 LINES BELOW NEEDS TO COMPILE"

int main()
{
	S<int> MySInt(5);
	S<char> MySChar('B');
	S<double> MySDouble(2.2);
	S<string> MySString(string("wow!"));
	S<vector<int>> MySVec(vector<int>(4, 99));

	cout << "MySInt: " << MySInt.val << endl;
	cout << "MySChar: " << MySChar.val << endl;
	cout << "MySDouble: " << MySDouble.val << endl;
	cout << "MySString: " << MySString.val << endl;

	cout << "MySVec: ";
	for (auto& e : MySVec.val)
		cout << e << " ";
	cout << endl;

	//THIS 5 LINES BELOW NEEDS TO COMPILE
	cout << "Returned val from get(): " << get(MySInt) << endl;
	cout << "Returned val from get(): " << get(MySChar) << endl;
	cout << "Returned val from get(): " << get(MySDouble) << endl;
	cout << "Returned val from get(): " << get(MySString) << endl;
	cout << "Returned val from get(): " << get(MySVec)[0] << endl;

	return 0;
}

So this is the function with decltype (assuming I am using it correctly) but doesn't work as well :/

template <typename T, typename R>
R& get(T& e)
{
	decltype(e.val) R;
	return e.val;
}

auto& as return type works just fine and those 5 lines compile, I was just wondering if there where any other ways :P 

Edited by MarcusAseth

Share this post


Link to post
Share on other sites

Moving to a new question, why is the code below not compiling if I uncomment one of the 4 commented-out lines ?

Function "set(Container, newValue, ID)"  takes a "MyContainer" class, a new value to set and an optional ID, just in case is a MyContainer<vector<int>>.

inside of it, takes the "val" of the MyContainer which returns wathever it is containing, in the example case is indeed a vector<int>&, but as soon as I try to use an operator<< or operator= on Vec, this thing doesn't compile...what am I not getting here? :S

template<typename T>
struct is_Container {
	static const bool value = false;
};

template<typename T, typename Alloc>
struct is_Container<vector<T,Alloc>> {
	static const bool value = true;
};

template <typename T, typename V>
void set(MyContainer<T>& e, V newVal, int ID = 0)
{
	T& Val = e.get();
	if (is_Container<T>::value)
	{
		cout << "is a vector" << endl;
		//cout << Val[ID] << endl;
		//Val[ID] = newVal;
	}
	else
	{
		cout << "is not a container" << endl;
		//cout << Val << endl;
		//Val = newVal;
	}
}

GcucCqV.png

Edited by MarcusAseth

Share this post


Link to post
Share on other sites
14 minutes ago, Kylotan said:

When you say something is "not compiling", it's normally polite to give the error message. :) That usually tells you everything you need to know...

 

Good point, I didn't yet sorted out the problem of my VC being in Italian, but I'll paste the error link :P

I'm getting 2 Compiler Error C2109 on line 52 and 53 where I try to  use Val[ID] inside my "if" block

Quote

subscript requires array or pointer type

And 2 Compiler Error C2679 on line 58 and 59 where I try to use Val inside my "else" block

Quote

binary 'operator' : no operator found which takes a right-hand operand of type 'type' (or there is no acceptable conversion)

But from the screenshot is clearly visible that Val is a vector<int>& therefore I don't get it.. well, since it doesn't run it is failing at compile time or before, so I assume this is because at that time is not known what Val will be, and is only know later on. So how do I get it to "assume" that I will provide a valid variable with those operators (operator[], operator<<, operator=) at run-time?! x_x

Edited by MarcusAseth

Share this post


Link to post
Share on other sites

Ok, trough commenting and uncommenting I think I've realized what is going on here:

template <typename T, typename V>
void set(MyContainer<T>& e, V newVal, int ID = 0)
{
	T& Val = e.get();
	if (is_Container<T>::value)
	{
		cout << "is a vector" << endl;
		//cout << Val[ID] << endl;
		//Val[ID] = newVal;
	}
	else
	{
		cout << "is not a container" << endl;
		//cout << Val << endl;
		//Val = newVal;
	}
}

When set() is instantiated with a first parameter that contain a MyContainer of basic type let's say int, then T is int and the expressions 

Quote

//cout << Val[ID] << endl;
        //Val[ID] = newVal;

becomes totally invalid (even though the control flow would never send us there).

On the other hand when I instantiate it with a MyContainer<vector<int>> as first parameter, then the expressions  

Quote

//cout << Val << endl;
 //Val = newVal;

make no sense at all. (cout of vector<int> and assignment of int to vector<int>

Working with templates can be confusing, it seems :P

How is this situation handled? Should Val be passed down to a function template with partial specialization for vectors and non vectors? I think that would work, but can't be too sure with this stuff :P

 

Edited by MarcusAseth

Share this post


Link to post
Share on other sites

Function overloading did it, so that must be the way to go, (I hope)

This is actually cool, because I can direct the flow of things toward the no inplicit conversion function overload! :D 

I'm discovering hot water here xD

template <typename T, typename V>
void set(MyContainer<T>& e, V newVal, size_t ID = 0)
{
	cout << "MyContainer specialization called" << endl;
	T& Val = e.get();
	set(Val, newVal, ID);
}

template <typename T>
void set(vector<T>& Val, T newVal, size_t ID = 0)
{
	cout << "vector specialization called" << endl;
	Val[ID] = newVal;
}
template <typename T, typename V>
void set(vector<T>& Val, V newVal, size_t ID = 0)
{
	cout << "NO IMPLICIT CONVERSION called" << endl;
}


template <typename T, typename V>
void set(T& Val, V newVal, size_t ID = 0)
{
	cout << "base type specialization called" << endl;
	Val = newVal;
}
int main()
{
	MyContainer<int> MySInt(5);
	MyContainer<vector<int>> MySVec(vector<int>(4, 99));

	cout << "MySInt: " << get(MySInt) << endl;
	set(MySInt, 22);
	cout << "MySInt: " << get(MySInt) << endl << endl << endl;

	cout << "MySVec: " << get(MySVec)[2] << endl;
	set(MySVec, 3.3, 2);
	cout << "MySVec: " << get(MySVec)[2] << endl << endl << endl;

	cout << "MySVec: " << get(MySVec)[2] << endl;
	set(MySVec, 64, 2);
	cout << "MySVec: " << get(MySVec)[2] << endl << endl << endl;
	return 0;
}

Output:

Quote

MySInt: 5
MyContainer specialization called
base type specialization called
MySInt: 22


MySVec: 99
MyContainer specialization called
NO IMPLICIT CONVERSION called
MySVec: 99


MySVec: 99
MyContainer specialization called
vector specialization called
MySVec: 64

 

Edited by MarcusAseth

Share this post


Link to post
Share on other sites
3 hours ago, MarcusAseth said:

S(T v) :val{ v }

I would just use the non-braced initializer list:

S(T v) :val(v)

I only use braced initializer list for zero-initializing arrays. Due to type deductions in C++11/14, braced initializer lists can be tricky.

FYI: setting up VS2017 can be cumbersome (you need lots of LMB clicks) for small C++ tests. You can use the Visual C++ compliant single header web compiler: http://webcompiler.cloudapp.net/.

Edited by matt77hias

Share this post


Link to post
Share on other sites
11 minutes ago, matt77hias said:

I would just use the non-braced initializer list:


S(T v) :val(v)

 

curly braces are the safest way, right?

If in there I'm initializing member variables, then { } prevents me to pass argument that would require an implicit conversion and possible truncation, so I just use those always and I never have to think about it because if I make a mistake, then things stop working right away :P

Edited by MarcusAseth

Share this post


Link to post
Share on other sites
9 minutes ago, MarcusAseth said:

curly braces are the safest way, right?

If in there I'm initializing member variables, then { } prevents me to pass argument that would require an implicit conversion and possible truncation, so I just use those and I never have to thing about it becuase if I make a mistake, then things stop working immediately :P

Safe? I guess (although in combination with std::initializer_list constructors?).

Obvious? No.

int a[3] = {1};
cout << a[0] << ',' << a[1] << ',' << a[2] << endl; 

 

What do you mean with the "prevention of passing arguments that would require implicit conversion"?

Edited by matt77hias

Share this post


Link to post
Share on other sites
28 minutes ago, matt77hias said:

Safe? I guess.

Obvious? No.


int a[3] = {1};
cout << a[0] << ',' << a[1] << ',' << a[2] << endl; 

 

Well, honestly, you example above to me seems obvious though :S

an array of 3 elements taking an initializer list, the initializer list to the right sets the first element to 1.

Quote

What do you mean with the "prevention of passing arguments that would require implicit conversion"?

I mean that the code below won't compile because it requires a conversion from double to int

	int a[3] = { 1.4 };
	cout << a[0] << ',' << a[1] << ',' << a[2] << endl;

If you accidentally passed a float variable you would have lost precision without noticing maybe, that's why I think is valuable to prefer it to the more loose ( )

EDIT: I compiled my example just to be sure, apparently it works, I must have got it wrong from the book o_O

Need to check.

EDIT2: ok, the safety I mentioned won't apply when you use it to initialize an array it seems, but it still work on all the future uses, and I think some safety is better than no safety at all (and it comes for free, so...I'll take it!) :D

Edited by MarcusAseth

Share this post


Link to post
Share on other sites
1 hour ago, MarcusAseth said:

EDIT2: ok, the safety I mentioned won't apply when you use it to initialize an array it seems, but it still work on all the future uses, and I think some safety is better than no safety at all (and it comes for free, so...I'll take it!) 

99% of my non-default/non-move/non-copy constructors are explicit, so except for implicit primitive-to-primitive conversions, I personally prefer calling the constructor straight away with () instead of {} (which calls a std::initializer_list constructor if present) in the constructor initializer list. Furthermore, my compiler uses the highest (reasonable, so not Wall) warning level to notify me of possible losses of precision due to for instance implicit primitive-to-primitive conversions.

Edited by matt77hias

Share this post


Link to post
Share on other sites

More template stuff.

I have some doubts regarding template, still. This compile and works, and if I understand it right, I need the operator overloads to be templates in case I try to do something like "Number<int>(3) + Number<float>(4.15)" which are two different type.

If I have those operator overloads just taking a plain Number& as argument, then things won't compile. 

And yet, operator>> and operator<< just work fine with a Number&, how does that make sense?!

EDIT: while I was writing this question, I thought that maybe is because inside a particular template instantiation itself, Number is defined to be the unique type for that Number<T>, therefore by saying Number I am actually calling the unique type of that particular instantiation?

If that was the case I could then just have a  return type Number& in the operator overloads instead of return type Number<T>& , can you guys confirm this is the case? :S

template<typename T>
class Number {
	T val;
public:
	//Constructors
	Number() :val{ 0 } {}
	Number(T v) :val{ v } {}

	//Methods
	T& get() { return val; }

	//Operators
	template<typename U>
	Number<T>& operator+(Number<U>& rhs) { val += rhs.get(); return *this; }
	template<typename U>
	Number<T>& operator-(Number<U>& rhs) { val -= rhs.get(); return *this; }
	template<typename U>
	Number<T>& operator*(Number<U>& rhs) { val *= rhs.get(); return *this; }
	template<typename U>
	Number<T>& operator/(Number<U>& rhs) { val /= rhs.get(); return *this; }

	friend ostream& operator<<(ostream& stream, Number& rhs) { stream << rhs.val; return stream; }
	friend istream& operator>>(istream& stream, Number& rhs) { stream >> rhs.val; return stream; }
};

int main()
{
	Number<double> myDouble(3.4);
	myDouble - Number<int>(6.6);
	cout << myDouble << endl << endl;
	
	cin >> myDouble;
	cout << myDouble << endl;

	return 0;
}

 

Share this post


Link to post
Share on other sites

Again, what are the compiler errors you get?

These operators are wrong anyway, because they are mutating the left-hand side when they should be returning a new value. If you want += semantics, you should define a += operator.

Share this post


Link to post
Share on other sites
2 minutes ago, MarcusAseth said:

No compile errors

 

28 minutes ago, MarcusAseth said:

If I have those operator overloads just taking a plain Number& as argument, then things won't compile.

 

Share this post


Link to post
Share on other sites

@Lactose Maybe I didn't explained myself properly, I am not considering that as the error, that's why is not even in my code example, and that makes sense because as I said

Quote

 I need the operator overloads to be templates in case I try to do something like "Number<int>(3) + Number<float>(4.15)" which are two different type.

In that case the compile error is obviously I have no operator overload between the 2 different types, if it was Number&.

The question is another one...and is about the absence of ant kind of compile errors :D

 

Edited by MarcusAseth

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now


  • Announcements

  • Forum Statistics

    • Total Topics
      628362
    • Total Posts
      2982266
  • Similar Content

    • By Kazuma506
      I am trying to recreate the combat system in the game Life is Feudal but make it more complex. The fighting system works by taking in the direction of the mouse movement and if you press the left click it will swing in that direction, though stab, overhead, left (up, down, left right) and right are the only swings that you can do. If you wanted to you could also hold the swing by holding left click so you are able to swing at the perfect moment in the battle. I want to change this so add in more swing directions but I also want to code this from scratch in Unreal. Can anyone give me any pointers or maybe a few snippets of code that work in Unreal that could help me start to implement this type of system?
       
       
    • By rXpSwiss
      Hello,
      I am sending compressed json data from the UE4 client to a C++ server made with boost.
      I am using ZLib to compress and decompress all json but it doesn't work. I am now encoding it in base64 to avoid some issues but that doesn't change a thing.
      I currently stopped trying to send the data and I am writing it in a file from the client and trying to read the file and decompress on the server side.
      When the server is trying to decompress it I get an error from ZLib : zlib error: iostream error
      My question is the following : Did anyone manage to compress and decompress data between a UE4 client and a C++ server ?
      I cannot really configure anything on the server side (because boost has its ZLib compressor) and I don't know what is wrong with the decompression.
      Any idea ?
      rXp
    • By noodleBowl
      I was wondering if someone could explain this to me
      I'm working on using the windows WIC apis to load in textures for DirectX 11. I see that sometimes the WIC Pixel Formats do not directly match a DXGI Format that is used in DirectX. I see that in cases like this the original WIC Pixel Format is converted into a WIC Pixel Format that does directly match a DXGI Format. And doing this conversion is easy, but I do not understand the reason behind 2 of the WIC Pixel Formats that are converted based on Microsoft's guide
      I was wondering if someone could tell me why Microsoft's guide on this topic says that GUID_WICPixelFormat40bppCMYKAlpha should be converted into GUID_WICPixelFormat64bppRGBA and why GUID_WICPixelFormat80bppCMYKAlpha should be converted into GUID_WICPixelFormat64bppRGBA
      In one case I would think that: 
      GUID_WICPixelFormat40bppCMYKAlpha would convert to GUID_WICPixelFormat32bppRGBA and that GUID_WICPixelFormat80bppCMYKAlpha would convert to GUID_WICPixelFormat64bppRGBA, because the black channel (k) values would get readded / "swallowed" into into the CMY channels
      In the second case I would think that:
      GUID_WICPixelFormat40bppCMYKAlpha would convert to GUID_WICPixelFormat64bppRGBA and that GUID_WICPixelFormat80bppCMYKAlpha would convert to GUID_WICPixelFormat128bppRGBA, because the black channel (k) bits would get redistributed amongst the remaining 4 channels (CYMA) and those "new bits" added to those channels would fit in the GUID_WICPixelFormat64bppRGBA and GUID_WICPixelFormat128bppRGBA formats. But also seeing as there is no GUID_WICPixelFormat128bppRGBA format this case is kind of null and void
      I basically do not understand why Microsoft says GUID_WICPixelFormat40bppCMYKAlpha and GUID_WICPixelFormat80bppCMYKAlpha should convert to GUID_WICPixelFormat64bppRGBA in the end
       
    • By HD86
      As far as I know, the size of XMMATRIX must be 64 bytes, which is way too big to be returned by a function. However, DirectXMath functions do return this struct. I suppose this has something to do with the SIMD optimization. Should I return this huge struct from my own functions or should I pass it by a reference or pointer?
      This question will look silly to you if you know how SIMD works, but I don't.
    • By pristondev
      Hey, Im using directx allocate hierarchy from dx9 to use a skinned mesh system.
      one mesh will be only the skeleton with all animations others meshes will be armor, head etc, already skinned with skeleton above. No animation, idle position with skin, thats all I want to use the animation from skeleton to other meshes, so this way I can customize character with different head, armor etc. What I was thinking its copy bone matrices from skeleton mesh to others meshes, but Im a bit confused yet what way I can do this.
       
      Thanks.
  • Popular Now