Advertisement

Problem getting iterator of container holding struct

Started by June 01, 2022 11:46 PM
6 comments, last by Juliean 2 years, 5 months ago

[Solution]

use typename when declaring the new alias:

using iterator = typename std::vector<DataStruct>::iterator;

[/solution]

This one is a bit convoluted…

The code below works:

template <class T>
class Something
{
public:
	struct DataStructure
	{
		int a;
		float b;
	};

	auto begin()
	{
		return indices.begin();
	}

private:
	std::vector<DataStructure>indices;
	T data;
};

void main()
{
	Something<int> thing;
	auto i = thing.begin();
}

I would like to get rid of the auto, ideally by declaring an alias like so (inside the public section of Something):

using iterator = std::vector<DataStructure>::iterator;

Doing this causes the compiler error “syntax error: identifier ‘iterator’”

This doesn't happen if

a) The class is not a template class, or

b) The vector holds a native type instead of a class or struct

This is using C++17 in visual studio 2022

Visual Studio's ability to hover your mouse over auto to determine what the underlying type is might help you here. With the auto version, look at what the expansion is.

In this case, it is not std::vector<int>::iterator, it is std::vector<Something<int>::DataStructure>::iterator.

If you changed the type of thing being held in the template class, you'll need to adjust the template parameters to your iterators to match.

Advertisement

You definately are missing a “typename” in your declaration, if you actually are declaring “iterator” inside class Something:

using iterator = typename std::vector<DataStructure>::iterator;

Since DataStructure is a dependant type of your template-class, the compiler doesn't fully know what the specialization of vector<DataStructure> looks like, so it cannot know whether vector<>::iterator is a typename, a constant, a member function etc… so you have to tell it that its supposed to a a typename.

Not 100% sure though, you didn't show the full stack of errors, and the full code that causes the error (the snipped you showed with vector<int>::iterator does not produce the error, as you said yourself).

See https://godbolt.org/z/zPxe78Y1j​ for what I mean.

Also, this is exactly the reason people pushed so hard for auto. It was the unnamable types of lambdas (whose underlying type is basically “block of code”) that forced the committee to accept them, but this is the reason people pushed. Building up convoluted template expressions, sometimes multiply nested template expansions, merely to name the type deep inside that all boils out to either a simple pointer or a simple iterator object is a lot of unnecessary work.

Even at companies that discourage use of auto, using them for complicated template names is generally acceptable practice. auto iterator = foo.begin(); is completely understandable.

The typename was the solution, thanks! It works perfectly now.

The std::vector<int> was a cut-and-paste error on my end, I've fixed the initial post.

I'm still getting used to auto. It's a great tool, but I don't like using it as a return, as it hides the intention. I'm also not sure how it will work with auto-generated documentation.

In general you should abstract away your internals. If the user of a class needs to know the implementation details you're over-exposing and making a brittle interface. If you change something about your inner details it would require an outer change, breaking the interface. You can do it of course, but it's bad practice.

Advertisement

methinks said:
I'm still getting used to auto. It's a great tool, but I don't like using it as a return, as it hides the intention. I'm also not sure how it will work with auto-generated documentation.

In addition to what frob says, a “begin” method is not something that a user really needs to know the return-type in the first place, as its rarely called manually these days, with range-based for loops, algorithms (and ranges in the future). So whether its “auto begin” or “iterator begin” doesn't make a difference, as “iterator” additionally doesn't tell much about what that actual type to expect is. So the user has to follow the “iterator” define to its declaration anyway, in which case they might as well look at the type of “indices” to see whats going on - especially if you didn't define another typedef for the vector<int>-type in the first place.

This topic is closed to new replies.

Advertisement