std::vector<UserType>
broken?Section: 17.6.3.4 [new.delete.placement] Status: New Submitter: Daniel Krügler Opened: 2013-09-18 Last modified: 2016-01-28
Priority: 3
View all other issues in [new.delete.placement].
View all issues with New status.
Discussion:
The library gives explicit permission in 16.4.5.2.1 [namespace.std] p2 that user code may explicitly instantiate a library template provided that the instantiations depend on at least one user-defined type:
A program may explicitly instantiate a template defined in the standard library only if the declaration depends on the name of a user-defined type and the instantiation meets the standard library requirements for the original template.
But it seems that the C++11 library is not specified in a way that guarantees such an instantiation to be well-formed if the minimum requirements of the library is not satisfied.
For example, in general, the first template parameter ofstd::vector
is not required to be
DefaultConstructible
in general, but due to the split of the single C++03 member function
with default argument
void resize(size_type sz, T c = T());
into
void resize(size_type sz); void resize(size_type sz, const T& c);
the effect is now that for a type ND
that is not DefaultConstructible
, such as
struct NP { NP(int); };
the explicit instantiation of std::vector<ND>
is no longer well-formed, because the attempt to
instantiate the single-argument overload of resize
cannot not succeed, because this function imposes
the DefaultInsertable
requirements and given the default allocator this effectively requires
DefaultConstructible
.
But DefaultConstructible
is not the only point, what about CopyConstructible
versus
MoveConstructible
alone? It turns out that currently the second resize
overload
would fail during an explicit instantiation for a type like
struct MO { MO() = default; MO(MO&&) = default; };
because it imposes CopyInsertable
requirements that end up being equivalent to the CopyConstructible
requirements for the default allocator.
resize
functions of std::vector
could be prevented from instantiation by defining them like this
with an implementation:
template<class = void> void resize(size_type sz) { […] } template<class = void> void resize(size_type sz, const T& c) { […] }
In this case, these functions could also be defined in a base class, but the latter approach won't work in all cases.
Basically such an implementation is required to constrain all member functions that are not covered by the general requirements imposed on the actual library template parameters. I tested three different C++11 library implementations and but none could instantiate for examplestd::list
, std::vector
, or std::deque
with
value types that are not DefaultConstructible
or only MoveConstructible
.
This issue is raised to clarify the current situation in regard to the actual requirements imposed on user-provided
types that are used to explicitly instantiate Library-provided templates. For example, the current Container requirements
impose very little requirements on the actual value type and it is unclear to which extend library implementations have
to respect that.
The minimum solution of this issue should be to at least realize that there is no fundamental requirement on
DefaultConstructible
for value types of library containers, because we have since C++03 the general
statement of 16.4.4.2 [utility.arg.requirements] ("In general, a default constructor is not required.").
It is unclear whether CopyConstructible
should be required for an explicit instantiation request, but
given the careful introduction of move operations in the library it would seem astonishing that a
MoveConstructible
type wouldn't suffice for value types of the container types.
In any case I can envision at least two approaches to solve this issue:
As indicated in LWG 2292, those function could get an explicit "Template Constraints:" element, albeit this promises more than needed to solve this issue.
The library could introduce a completely new element form, such as "Instantiation Constraints:" that
would handle this situation for explicit instantiation situations. This would allow for simpler techniques
to solve the issue when explicit instantiation is required compared to the first bullet, because it would not
(necessarily) guarantee SFINAE-friendly expression-wellformedness, such as inspecting the expression
std::declval<std::vector<ND>&>.resize(0)
in an unevaluated context.
It should be noted that the 2013-08-27 comment to LWG 2193 could be resolved by a similar solution as indicated in this issue here.
Proposed resolution: