Please consider the code below, which compiles in VS2012 but fails in VS2010 with the error
1>------ Build started: Project: testconstinit, Configuration: Debug Win32 ------
1> testconstinit.cpp
1>c:\program files (x86)\microsoft visual studio 10.0\vc\include\xmemory(48): error C2248: 'std::unique_ptr<_Ty>::unique_ptr' : cannot access private member declared in class 'std::unique_ptr<_Ty>'
1> with
1> [
1> _Ty=int
1> ]
1> c:\program files (x86)\microsoft visual studio 10.0\vc\include\memory(2347) : see declaration of 'std::unique_ptr<_Ty>::unique_ptr'
1> with
1> [
1> _Ty=int
1> ]
1> c:\program files (x86)\microsoft visual studio 10.0\vc\include\xmemory(197) : see reference to function template instantiation 'void std::_Construct<std::unique_ptr<_Ty>,const std::unique_ptr<_Ty>&>(_Ty1 *,_Ty2)' being compiled
1> with
1> [
1> _Ty=int,
1> _Ty1=std::unique_ptr<int>,
1> _Ty2=const Movable &
1> ]
1> c:\program files (x86)\microsoft visual studio 10.0\vc\include\xmemory(196) : while compiling class template member function 'void std::allocator<_Ty>::construct(std::unique_ptr<int> *,const _Ty &)'
1> with
1> [
1> _Ty=Movable
1> ]
1> c:\program files (x86)\microsoft visual studio 10.0\vc\include\vector(421) : see reference to class template instantiation 'std::allocator<_Ty>' being compiled
1> with
1> [
1> _Ty=Movable
1> ]
1> c:\program files (x86)\microsoft visual studio 10.0\vc\include\vector(481) : see reference to class template instantiation 'std::_Vector_val<_Ty,_Alloc>' being compiled
1> with
1> [
1> _Ty=Movable,
1> _Alloc=std::allocator<Movable>
1> ]
1> c:\users\zadirion\documents\visual studio 2010\projects\testconstinit\testconstinit\testconstinit.cpp(34) : see reference to class template instantiation 'std::vector<_Ty>' being compiled
1> with
1> [
1> _Ty=Movable
1> ]
1> c:\users\zadirion\documents\visual studio 2010\projects\testconstinit\testconstinit\testconstinit.cpp(81) : see reference to class template instantiation 'LazyValue<T>' being compiled
1> with
1> [
1> T=Container
1> ]
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
The code:
#include "stdafx.h"
#include <vector>
#include <memory>
#include <functional>
#include <deque>
using namespace std;
typedef std::unique_ptr<int> Movable;
typedef vector<Movable> Container;
typedef vector<Movable> (*MakeType)();
template <class T, class Initializer = function<T(void)> >
struct LazyValue
{
LazyValue(Initializer aInit) : mInit(aInit) {}
void Init() const
{
m = mInit();
}
private:
mutable T m; // <-- compiler error at this line
Initializer mInit;
LazyValue operator=(const LazyValue & aOther)
{
}
};
template <class T>
struct GenericList
{
std::deque<T> mValues;
GenericList(){}
GenericList & operator()(T && aValue)
{
mValues.push_back(std::move(aValue));
return *this;
}
template <class Container>
operator Container()
{
auto it = mValues.begin();
auto endIt = mValues.end();
Container c;
for ( ; it != endIt; it++ )
{
c.push_back(std::move(*it));
}
return std::move(c);
}
};
template <class T>
GenericList<T> ListOfRValues()
{
return GenericList<T>();
}
int _tmain(int argc, _TCHAR* argv[])
{
const LazyValue<Container> s = []()->Container{
return ListOfRValues<Movable>()
(Movable(new int) )
(Movable(new int) )
(Movable(new int) );
};
return 0;
}
Can anyone point with a link to the bug submitted to Microsoft maybe, or an explanation on what the compiler bug is actually, I am trying to understand which part of the code exactly is troubling the compiler. Also, what workaround do we have for this?
Thank you!
This code should not compile.
The problem is in the fact that you are using copy-initialization, which may require (if the compiler is not eliding it) the construction of a temporary object of type
LazyValue<Container>, which is then moved into the initialized objects.From Paragraph 8.5/14 of the C++11 Standard:
Moreover, according to Paragraph 8.5/16:
Let’s assume for the moment that your compiler does not elide the copy/move (the compiler is allowed, but not required, to do so).
Your class template doesn’t define any move constructor, and the implicitly generated copy constructor will be selected for constructing the object
sfrom the temporary which has been constructed from the lambda on the right side of the initialization.Unfortunately, your class has a member variable of type
Container, which is a container of non-copyable elements. Hence, instantiation of the implicitly generated copy-construction will fail, which explains the error you are getting.You should use direct-initialization instead:
Let’s now consider the case where the compiler does choose to elide the copy/move. There is a requirement in the C++11 Standard on this behavior, coming from Paragraph 12.8/32:
The key term here is accessible. The instantiation of the implicitly generated copy-constructor cannot succeed, because the object to be copied contains a non-copyable sub-object; which necessarily makes the copy-constructor inaccessible, because it can never be instantiated. Hence, a conforming compiler shall refuse to compile the code, and I believe this is qualifies as a bug in VS2012.
P.S.: Also, mind the fact that you are violating the so-called Rule of Three (apart from having an overloaded copy-assignment operator that returns nothing, while it should probably return
*this).