Visual Studio std::vector::emplace_back c++11 vs c++17

Started by
2 comments, last by Grumple 6 years, 6 months ago

I have a general c++ template programming question...I've tried to setup a class that used a nested template type T, along with variadic template arguments for a constructor of T, to ensure I can add items to a custom container class without any unnecessary heap usage/copy/move operations.  

Here is my test container code, using perfect forwarding and std::vector::emplace_back() to create items 'in-place' with little overhead (TemplateContainerTest.h):


#pragma once

#include <vector>
#include <unordered_map>
#include <utility>

template <typename T>
class TemplateContainerTest
{
public:
	TemplateContainerTest() = default;
	virtual ~TemplateContainerTest() = default;

	template<typename... ItemArgs>
	void AddItem( ItemArgs&&... itemArgs )
	{
		m_Container.emplace_back( std::forward<ItemArgs>( itemArgs )... );
	}
protected:
	template <typename T>
	class ItemTracker 
	{
	public:
		template<typename... ItemArgs >
		ItemTracker( ItemArgs&&... itemArgs ):
			m_Item( std::forward<ItemArgs>( itemArgs )... )
		{
		}

		bool m_IsValid = false;

		T m_Item;
	};

	std::vector< ItemTracker<T> > m_Container;
};

And here is some code to exercise the template above (main.cpp):


#include "stdafx.h"
#include <stdint.h>

#include "CNTemplateContainer.h"

class TestItemOfInterest
{
public:
	TestItemOfInterest( uint32_t itemVal ):
		m_ItemVal( itemVal )
	{
	}

	TestItemOfInterest( TestItemOfInterest && other )
	{
		m_ItemVal = other.m_ItemVal;

		other.m_ItemVal = 0;
	}

	TestItemOfInterest() = default;
	virtual ~TestItemOfInterest() = default;

	uint32_t GetVal() { return m_ItemVal; }
protected:
	uint32_t m_ItemVal = 0;
};


int _tmain(int argc, _TCHAR* argv[])
{
	TemplateContainerTest<TestItemOfInterest> tmpContainer;

	tmpContainer.AddItem( 42 );

	return 0;
}

Here is the kicker: in Visual Studio 2013, the code above fails to compile with the following error:

templatecontainertest.h(28): error C2664: 'TestItemOfInterest::TestItemOfInterest(const TestItemOfInterest &)' : cannot convert argument 1 from 'TemplateContainerTest<TestItemOfInterest>::ItemTracker<T>' to 'uint32_t'

However, in Visual Studio 2017 it compiles fine.   For some reason, the perfect forwarding mechanism of Visual Studio 2013 seems to try to send the 'ItemTracker' into the T() constructor instead of just the arguments from outside.   I see that the VS 2017 std::vector::emplace_back signature/implementation changed, but I can't understand why it works now/didn't work before...

Any insight would be appreciated as I don't trust this at all without understanding the underpinning issues...

Advertisement

Might have been a bug in 2013 that was fixed for 2015/17.  VS 2013 didn't fully support C++11 features.  I don't even think 2017 is fully compliant yet.  Though they're close from what I read.  Any reason you need to test this on a 4 year old compiler?  Just curious.

"Those who would give up essential liberty to purchase a little temporary safety deserve neither liberty nor safety." --Benjamin Franklin

Yeah that could very well be it, just looking to rule out that it 'shouldn't compile' for some reason.

I have no good excuse for the old compiler other than this being the first issue that has really made me feel the need to change.  Having seen this code compile on a friends VS2017 setup, I will likely be be updating asap...

Thanks!

This topic is closed to new replies.

Advertisement