Can classes place constraints on templates as well as the other way around?

111 Views Asked by At

Imagine two smart pointer templates, smart_ptr and smart_ptr_with_abort.

  • smart_ptr simply calls delete when the final reference to the object it holds is released
  • smart_ptr_with_abort calls a class method Abort() before deleting the object.

My object object has an Abort() method that must be called before the object is deleted.

Using concepts I can make smart_ptr_with_abort fail to compile if it's being used with an object that doesn't have an Abort() method.

Is there a way to achieve the reverse situation, where smart_ptr<object> would fail to compile but smart_ptr_with_abort<object> would succeed?

1

There are 1 best solutions below

0
Chukwujiobi Canon On BEST ANSWER

With more pondering on the question, I have chosen to approach the question differently thanks to Elliott.

The OP starts out this way and I quote:

Imagine two smart pointer templates, smart_ptr and smart_ptr_with_abort

Forgive me for my approach but instead let’s imagine two classes that can take an arbitrary type upon construction.[1]

The constraint is that one class A must only take a type that has a method Abort() while another class B will only take a type that does not have the method Abort(). If class A is specialized with a type that doesn’t have the member function Abort(), the program fails to compile. And if class B takes a type that has the member function Abort(), the program also fails to compile.

Using concepts I can make smart_ptr_with_abort[2] fail to compile if it's being used with an object that doesn't have an Abort() method.

Without your code I will write one approach here for posterity.

template<class T>
concept Abortable = requires(T t) { {t.Abort()}; };

template<Abortable T>
class A {/* implementation */}

Is there a way to achieve the reverse situation, where smart_ptr<object>[3] would fail to compile but smart_ptr_with_abort<object> would succeed?

That is by using ”A Logical Negation expression”. We can simply negate the other concept to produce a new concept.

template<class T>
concept NotAbortable = !Abortable<T>;

template<NotAbortable T>
class B {/* implementation */}

More on Logical negation expression:

5 [Note: A logical negation expression (7.6.2.1) is an atomic constraint; the negation operator is not treated as a logical operation on constraints. As a result, distinct negation constraint-expressions that are equivalent under 13.7.6.1 do not subsume one another under 13.5.4. Furthermore, if substitution to determine whether an atomic constraint is satisfied (13.5.1.2) encounters a substitution failure, the constraint is not satisfied, regardless of the presence of a negation operator. [Example:

template <class T> concept sad = false;

template <class T> int f1(T) requires (!sad<T>);

template <class T> int f1(T) requires (!sad<T>) && true;
                   int i1 = f1(42); // ambiguous, !sad<T> atomic constraint expressions (13.5.1.2) are not formed from the same expression

template <class T> concept not_sad = !sad<T>;

template <class T> int f2(T) requires not_sad<T>;

template <class T> int f2(T) requires not_sad<T> && true;
                   int i2 = f2(42); // OK, !sad<T> atomic constraint expressions both come from not_sad

template <class T> int f3(T) requires (!sad<typename T::type>); 
                   int i3 = f3(42); // error: associated constraints not satisfied due to substitution failure

template <class T> concept sad_nested_type = sad<typename T::type>;

template <class T> int f4(T) requires (!sad_nested_type<T>);
                   int i4 = f4(42); // OK, substitution failure contained within sad_nested_type

Here,

requires (!sad<typename T::type>) requires that there is a nested type that is not sad, whereas

requires (!sad_nested_type<T>) requires that there is no sad nested type. — end example ] — end note ]

—-ISO/IEC JTC1 SC22 WG21 N4860 13.5.1.1


[1] This approach shields us from having our design criticized. I admit I did that in my first answer and any critic would do the same I believe.

[2] That will be class A in our illustration.

[3] That will be class B in our illustration.