I have a template class Property, that wraps around other types:
template <typename T>
class Property
{
private:
T value;
public:
Property() = default;
Property(const T& initialValue)
: value(initialValue) {}
virtual ~Property() = default;
//Make this class non-copyable.
Property(const Property&) = delete;
Property& operator=(const Property&) = delete;
virtual Property& operator=(const T& other)
{
value = other;
return *this;
}
//... a bunch of other unimportant stuff
}
Visual Studio 15.7.6 and a few other compilers are perfectly happy with
{ //function or class definition (default member initialization)
Property<int> prop = 5;
}
However, (a slightly modified for proprietary compilation target) GCC 4.9.4 fails on the above declaration:
Error GD4849D22 use of deleted function
'Property<T>::Property(const Property<T>&) [with T = int]'
It appears the compiler is attempting to construct a RValue Property and then use the deleted copy constructor instead of simply using the type appropriate constructor.
Is this a case of GCC being over cautious?
Property<int> prop(5); //explicit constructor call - valid with GCC Compiler
Property<int> myOtherProp;
myOtherProp = 5; //Also fine (obviously)
Or is it a case of MSVC playing fast and loose and doing something the standard says it shouldn't or doesn't have to?
Unfortunately I cant update my GCC version. Because the workaround exists I'm looking more for the "why" this happens than anything else.
I believe what happens here is guaranteed copy elision at work (which is a C++17 feature). A member declaration like
struct A
{
Property<int> prop = 5;
};
means that the member will be initialized via copy-initialization. Using your conversion constructor, first, a temporary Property
object would be constructed from 5
, from which the actual property is then constructed. Since Property
is not movable, the copy constructor is invoked, which is deleted. While the compiler was allowed to elide this copy even before C++17 (and any reasonable compiler since basically forever will have done so), it was still required to enforce all constraints such as existence and accessibility of any necessary constructors as if the copy was made. C++17 does away with all that by basically making copy elision mandatory.
Quick test with clang here; if you switch the language standard to c++17, you'll see it suddenly work…