value_type ?

Hello, I am wondering if someone could help me out with what value_type does in this code. Is it causing a new variable called res to be created that is a sting and setting it to equal that of what the string s holds? and if so, why would this be done this way in the iteration part. why not just iterate s?

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
  template <typename T>
void exercise(int size, string commonname)
{
	T v;
	T::template value_type res1("starting entry");
	v.push_back(res1);
	for (int i = 0; i < size; i++)
	{
		int r = (int)rand();
		string s;
		int ss = r % 100;
		s.reserve(ss);
		std::generate_n(std::back_inserter(s), ss, [ss]() {return (char)('!' + ss / 2); });
		T::template value_type res(s);
		bool inserted = false;
		for (auto it = begin(v); it != end(v); it++)
		{
			if (*it > res)
			{
				v.insert(it, res);
				inserted = true;
				break;
			}
		}
		if (!inserted)
			v.push_back(res);
	}

	auto it = max_element(begin(v), end(v));
	T::template value_type maxV = *it;
	it = min_element(begin(v), end(v));
	T::template value_type minV = *it;

	bool sorted = is_sorted(begin(v), end(v));
}
The syntax is wrong. The keyword 'template' can't be used in that position. What whoever wrote the code meant to use was 'typename'. All the value_type lines should look more like this:

 
	typename T::value_type res1("starting entry");

T::value_type is whatever it has been defined as in the type T.
For instance if T is a vector<int> then value_type will be int.
Of course you wouldn't be able to set an int to "starting entry", so apparently value_type is assumed to be some kind of string.
The code looks pretty bad.
Where did you get it?
Last edited on
Just to clarify it was the value_type in line 14 i was mostly asking about. But i guess it is the same for line 5.

The code is from a pluralsight tutorial, which I pay for every month to learn from, so its not exactly great that the code is wrong.
Yes, the change is the same for all lines like that. Remove 'template' and put 'typename' in front of the whole line. Alternatively you could change all but the first one to just use 'auto'. Still, they're pretty much assuming the type is string so they may as well just use string.

Also note that the parameter commonname and the local variable sorted are unused.

so its not exactly great that the code is wrong

You should let them know.

Do you know what it's supposed to do?
Last edited on
The template keyword sometimes can appear there. It's used to disambiguate member templates in a dependent context. The most well-known example is probably
typename Allocator::template rebind<U>::other
For the pre-C++17 definition of Allocator.
In this case, the template is required to tell the compiler that < means "start a template argument list" and not "less-than".

Fortunately the template disambiguator doesn't appear much any more because alias templates don't need them.

There is some implementation divergence. MSVC compiles this example (and might compile the above code), because it still looks up names incorrectly.
1
2
3
4
struct foo { template <typename T> struct value_type { value_type(T) {} }; };
template <typename T> void exercise() 
{ T::template value_type res1("foo"); }
template void exercise<foo>();


One correct way to write that code is
1
2
3
4
struct foo { template <typename T> struct value_type { value_type(T) {} }; };
template <typename T> void exercise() 
{ typename T::template value_type<char const*> res1("foo"); }
template void exercise<foo>();


If I'm reading the standard correctly, the template argument list is required. This seems to create a weird corner case where class-template argument deduction just won't work if you have to write template.
https://eel.is/c++draft/temp.names#3

No clue how MSVC handles template in this context, because it compiles this too:
1
2
3
struct foo { struct value_type { }; };
template <typename T> void exercise() { T::template value_type res1{}; }
template void exercise<foo>();

https://godbolt.org/z/GxrTfv
Last edited on
Thanks mbozzi. That's interesting. I've never seen 'template' used like that before.
Still, the code in the first post is pretty crappy.
Without knowing the original purpose, a possible 'clean-up' of the code could be:

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
#include <string>
#include <vector>
#include <algorithm>
#include <iterator>
#include <iostream>
#include <random>

template <typename T>
void exercise(size_t size)
{
	static auto Rand {std::mt19937 {std::random_device {}()}};
	static auto randNo {std::uniform_int_distribution<size_t> {1, 99}};

	using Type = typename T::value_type;

	//const Type res1 {"starting entry"};
	T v;
	//v.push_back(res1);

	for (size_t i = 0; i < size; ++i) {
		const auto ss {randNo(Rand)};
		const Type res(ss, '!' + ss / 2);
		bool inserted {};

		for (auto it = begin(v); it != end(v); ++it)
			if (*it > res) {
				v.insert(it, res);
				inserted = true;
				break;
			}

		if (!inserted)
			v.push_back(res);
	}

	const auto [minV, maxV] {minmax_element(begin(v), end(v))};
	const bool sorted {is_sorted(begin(v), end(v))};

	std::cout << "min: " << *minV << '\n';
	std::cout << "max: " << *maxV << '\n';
	std::cout << "Is sorted: " << std::boolalpha << sorted << '\n';
}

int main()
{
	exercise<std::vector<std::string>> (10);
}

Thank you for all the responses, I had to take a couple of days away from it, so just checking now.

As the courses get further on on pluralsight it seems that the lecturer of the classes seem to be less responsive, as people have seem to have commented on the code already without response.

The code was meant to teach move semantics. There was more to the code, but didnt think it was too important when I was just asking about value_type. But im having an issue now understanding the move semantics, not sure if i am best asking a new question, or just posting again since the code is the same, so i will just do that. The full code is below.

What I am wondering is, in the line v.insert(it, res); the copy constructor is called, is that making a copy of res? Is that making a temp object of res? Then there is a move assignment that happens with that value, which is the temp value being moved from the temp object into the vector.

Also, when the first element 'starting entry' needs to move, the move constructor is called - i was told that this is called when an object is created from a object that is about to go away. So is this called because nothing exists in the position that starting entry is moving to, and starting entry is about to be removed from its first position?

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


#include "Resource.h"
#include <iostream>
#include <iterator>
#include <chrono>
using std::chrono::system_clock;
using std::chrono::steady_clock;
using std::chrono::duration_cast;
using std::chrono::seconds;
using std::chrono::milliseconds;

#include <string>
using std::string;

#include <vector>
#include <list>
using std::vector;
using std::list;

#include <algorithm>

template <typename Func>
long long TimeFunc(Func f)
{
	auto begin = steady_clock::now();
	f();
	auto end = steady_clock::now();

	return duration_cast<milliseconds>(end - begin).count();
}


template <typename T>
void exercise(int size, string commonname)
{
	T v;
	v.reserve(50);
	//T::template value_type res1("starting entry");
	typename T::value_type res1("starting entry");
	v.push_back(res1);
	for (int i = 0; i < size; i++)
	{
		std::cout << "string container\n";
		int r = (int)rand();
		string s;
		int ss = r % 100;
		s.reserve(ss);
		std::generate_n(std::back_inserter(s), ss, [ss]() {return (char)('!' + ss / 2); });
		T::template value_type res(s);
		bool inserted = false;
		for (auto it = begin(v); it != end(v); it++)
		{
			
			if (*it > res)
			{
				v.insert(it, res);		
				inserted = true;
				
										
				break;
			}
		}
		if (!inserted)
			v.push_back(res);

	}

	auto it = max_element(begin(v), end(v));
	T::template value_type maxV = *it;
	it = min_element(begin(v), end(v));
	T::template value_type minV = *it;

	bool sorted = is_sorted(begin(v), end(v));
}


int main()
{
	const int size = 1000;

	auto vectormilliseconds = TimeFunc([&]() {exercise<vector<Resource>>(size, "vector"); });
	//auto listmilliseconds = TimeFunc([&]() {exercise<list<Resource>>(size, "list"); });


}



resource.h
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

#pragma once
#include <string>
#include <fstream>
#include <iostream>

class Resource
{
private:
	std::string name;
public:
	Resource(std::string n);
	Resource(const Resource& r);
	Resource& operator=(const Resource& r);
	Resource(Resource&& r);
	Resource& operator=(Resource&& r);
	~Resource(void);
	bool operator>(const Resource& r) { return name > r.name; }
	bool operator<(const Resource& r) { return name < r.name; }
	bool operator==(const Resource& r) { return name == r.name; }

	friend std::ostream& operator<<(std::ostream& s, const Resource& obj);
	std::string GetName() const { return name; }
};



resource.cpp

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
#include "Resource.h"
#include <iostream>
using std::cout;
using std::string;

Resource::Resource(string n) :name(n)
{
	cout << "constructing " << name << '\n';
}

Resource::Resource(const Resource& r) : name(r.name)
{
	cout << "copy constructing " << name << '\n';
}

Resource& Resource::operator=(const Resource& r)
{
	//if class contained true Resources, clean up existing ones
	//before setting new values
	name = r.GetName();
	cout << "copy assigning " << name << '\n';
	return *this;
}

Resource::Resource(Resource&& r) :name(std::move(r.name))
{
	cout << " move construuctor " << name << "\n";
}

Resource& Resource::operator=(Resource&& r)
{
	if (this != &r)
	{
		name = std::move(r.name);
		r.name.clear();
		cout << "move assigning " << name << '\n';
	}
	return *this;
}

Resource::~Resource(void)
{
	//	cout << "destructing " << name << '\n';
}

std::ostream& operator<<(std::ostream& s, const Resource& obj)
{
	s << obj.name << " ";
	return s;
}


Topic archived. No new replies allowed.