.. ot-topic:: cxx11.new_language_features.concepts.writing_concepts
   :dependencies: cxx11.new_language_features.concepts.intro

.. include:: <mmlalias.txt>


Writing Your Own Concepts
=========================

.. contents::
   :local:

Starting Point: Good Old Function
---------------------------------

.. toctree::
   :hidden:

   writing-concepts/example-1-good-old-func

.. sidebar::

   **Source**

   * :doc:`writing-concepts/example-1-good-old-func`

* N-dimensional hypotenuse
* ``std::vector<double>``
* Using index based iteration, only too keep requirements to a minimum
* Requirements

  * ``.size()``
  * ``.operator[]()``

.. code-block:: c++

   double hypotenuse(const std::vector<double>& v)
   {
       double sumsq = 0;
       for (size_t i=0; i<v.size(); ++i)                  // <--- vector has .size()
           sumsq += v[i]*v[i];                            // <--- vector has []
       return std::sqrt(sumsq);
   }

Need Template
-------------

.. toctree::
   :hidden:

   writing-concepts/example-2-need-template

.. sidebar::

   **Source**

   * :doc:`writing-concepts/example-2-need-template`

* ``point2d``: members ``x, y`` (and potentially some operations on a
  point, but that is not the point here)
* How to fit that into ``hypotenuse()``?
* |longrightarrow| implement what's *required* by ``hypotenuse()``
  (``.size()`` and ``.operator[]()``)
* |longrightarrow| Turn ``hypotenuse()`` into a template
* *Straightforward*

.. code-block:: c++

   class point2d
   {
   public:
       point2d(double x, double y)
       : _x{x}, _y{y} {}
   
       size_t size() const { return 2; }                  // <--- point2d has .size() too
       double operator[](size_t i) const                  // <--- point2d has [] too
       {
           if (i==0) return _x;
           if (i==1) return _y;
           return 666;
       }
   private:
       double _x, _y;
   };

Error: Requirement Not Fulfilled
--------------------------------

.. code-block:: c++

   class point2d
   {
       // size_t size() const { return 2; }               // <--- requirement *not* fulfilled
   };

* Uncomment ``.size()``
* Error message is relatively clear (see below)
* ``error: ‘const class point2d’ has no member named ‘size’``
* Can be worse though; for example if helper types (possibly nested a
  dozen levels deep) are instantiated by the implementation

.. code-block:: console

   example-3-requirement-not-fulfilled.cpp: In instantiation of ‘double hypotenuse(const V&) [with V = point2d]’:
   example-3-requirement-not-fulfilled.cpp:39:28:   required from here
   example-3-requirement-not-fulfilled.cpp:9:26: error: ‘const class point2d’ has no member named ‘size’
       9 |     for (size_t i=0; i<v.size(); ++i)
         |                        ~~^~~~

Concept: ``has_size``
---------------------

.. toctree::
   :hidden:

   writing-concepts/example-4-concept-has-size

.. sidebar::

   **Source**

   * :doc:`writing-concepts/example-4-concept-has-size`

* Implement concept ``has_size``
* Requires that any object of ``V`` has a ``.size()`` method
* |longrightarrow| i.e. the expression ``v.size()`` *compiles*

.. code-block:: c++

   template <typename V>
   concept has_size = requires(V v) {
       v.size();                                          // <--- compiles
   };

* Concept usage: good old full explicit template

.. code-block:: c++

   template <typename V>
   requires has_size<V>
   double hypotenuse(const V& v) { /*...*/ }

* Concept usage: good old full explicit template (after declaration)

.. code-block:: c++

   template <typename V>
   double hypotenuse(const V& v) requires has_size<V> { /*...*/ }

* Concept usage: abbreviated function templates

.. code-block:: c++

   double hypotenuse(const has_size auto& v) { /*...*/ }

Concept: ``index_returns_double``
---------------------------------

.. toctree::
   :hidden:

   writing-concepts/example-5-concept-index-ret-double

.. sidebar::

   **Source**

   * :doc:`writing-concepts/example-5-concept-index-ret-double`

* Hmm ... what if elements are not ``double``?
* |longrightarrow| Somebody could use ``hypotenuse()`` on something
  that has ``int`` coordinates
* Could we check this?

*Ruin the whole thing ...*

* Modify object initialization to take real double values,
  e.g. ``{3.5L, 4.5L}``
* |longrightarrow| Result not straight 5 anymore
* Modify ``point2d::operator[]()`` to return ``int``
* |longrightarrow| Result straight 5 again

*Concept* ``index_returns_double``

.. sidebar::

   **Documentation**

   * `std::same_as
     <https://en.cppreference.com/w/cpp/concepts/same_as>`__
   * `std::commone_reference_with
     <https://en.cppreference.com/w/cpp/concepts/common_reference_with>`__
   * <concepts>: `Standard library header
     <https://en.cppreference.com/w/cpp/header/concepts>`__
   * `std::vector
     <https://en.cppreference.com/w/cpp/container/vector>`__

* First use ``std::same_as<double>``
* |longrightarrow| Constraint check fails:
  ``std::vector::operator[]()`` is *not* same as ``double`` (rather, it returns ``double&``)
* Solution: ``std::commone_reference_with<double>``

.. code-block:: c++

   template <typename V>
   concept index_returns_double = requires(V v) {
       { v[0] } -> std::common_reference_with<double>;
   };

* Argh: checking multiple constraints is not possible with abbreviated
  function templates
* |longrightarrow| fall back to ordinary template syntax, and its
  ``requires`` clause