I am trying to get to grips with the specifics of the (C 20) standards requirements for container classes with a view to writing some container classes that are compatible with the standard library. To begin looking into this matter I have looked up the references for named requirements, specifically around container requirements, and have only found one general container requirement called Container
given by the standard. Reading this requirement has given my two queries that I am unsure about and would like some clarification on:
The requirement for the expression
a == b
for two container typeC
has as precondition on the element typeT
that it is equality comparable. However, noted later on the same page under the header 'other requirements' is the explicitly requirement thatT
be always equality comparable. Thus, on my reading the precondition for the aforementioned requirement is redundant and need not be given. Am I correct in this thinking, or is there something else at play here that I should take into account?I was surprised to see explicit requirements on
T
at all: notably the equality comparable requirement above and the named requirement destructible. Does this mean it is undefined behaviour to ever construct standard containers of types failing these requirements, or only to perform certain standard library function calls on them?
Apologies if these two questions sound asinine, I am currently trying to transition my C knowledge from a place of having a basic understanding of how to use features to a robust understanding so that I may write good generic code. Whilst I am trying to use (a draft of) the standard to look up behaviour where possible, its verbiage is oft too verbose for me to completely understand what is actually being said.
In an attempt to seek the answer I cooked up a a quick test .cpp
file to try an compile, given below. All uncommented code compiles with MSVC compiler set to C 20. All commented code will not compile, and visa versa all uncommented code will. It seems that what one naively thinks should work does In particular:
- We cannot construct any object without a destructor, though the objects type is valid and can be used for other things (for example as a template parameter!)
- We cannot create an object of
vector<T>
, whereT
has no destructor, even if we don't attempt to create any objectsT
. Presumably because creating the destructor forvector<T>
tries to access a destructor forT
. - We can create an object of type
vector<T>
,T
whereT
has no operator==
, so long as we do not try to use operator==
, which would requireT
to have operator==
.
However, just because my compiler lets me make an object of vector<T>
where T
is not equality-comparable does not mean I have achieved standards compliant behaviour/ all of our behaviour is not undefined - which is what I want I concerned about, especially as at least some of the usual requirements on the container object have been violated.
Code:
#include<vector>
#include<iostream>
//A basic class with its == operator removed
class MyNonEqualityComparableClass
{
//Destroy any possible == operator, for good measure
template<typename T>
auto operator==(T&& t) = delete;
public:
//Give the test struct a value so we mute the class and check
bool Mytestvalue = true;
};
class MyNonDestructableClass
{
~MyNonDestructableClass() = delete;
};
// A basic class template with no functionality added
template<typename T>
class MyTemplateClass
{};
int main()
{
//1. Non-destructable Class Test.
{
//Compiler Grumbles if uncommented, no destructor is inaccessable for the class
//MyNonDestructableClass a, b;
//Class Dependent On my Nondestructable Class
MyTemplateClass < MyNonDestructableClass> mtc;
//Compiler Grumbles if uncommented, as class tries to acceess the inacessable destructor of MyNonDestructableClass
//auto u = std::vector< MyNonDestructableClass>();
}
//2. Non-Equality-Comparable Class Test.
{
MyNonEqualityComparableClass x, y;
//Compiler Grumbles if uncommented
//x == y;
//Compiler Fine with below
auto u = std::vector<MyNonEqualityComparableClass>();
auto v = std::vector<MyNonEqualityComparableClass>();
u.push_back(x);
v.push_back(y);
x.Mytestvalue = !x.Mytestvalue;
//Compiler Grumbles if uncommented
//u == v;
std::cout << "The value of x.Mytestvalue is: \t" << std::boolalpha << x.Mytestvalue << std::endl;
}
}
My output:
The value of x.Mytestvalue is: false
CodePudding user response:
If the members of a container are not destructible, then the container could never do anything except add new elements (or replace existing elements). erase
, resize
and destruction all involve destroying elements. If you had a type T
that was not destructible, and attempted to instantiate a vector<T>
(say), I would expect that it would fail to compile.
As for the duplicate requirements, I suspect that's just something that snuck in when the CppReference folks wrote that page. The container requirements in the standard mention (in the entry for a == b
) that the elements must be equality comparable.