c++operator-overloadingc++20c++-concepts

c++20 how to use concepts to check for overloaded operators


I'm currently struggeling to make my concept check for overloaded operators. I managed to check for the "normal" <=> operator but I'm failing to check for the <=> operator which uses a std::shared_ptr and compares it with T

#include <compare>
#include <list>
#include <memory>
#include <concepts>
#include <stdexcept>

template<typename T>
concept Sortable_T = requires(T a, T b)
{
  { a <=> b } -> std::same_as<std::strong_ordering>;
  { a <=> std::shared_ptr(b) } -> std::same_as<std::strong_ordering>; //< this restraint is causing the problem
};

template<Sortable_T Element_T>
class SortableList
{
  public:
  //some other stuff goes first...
  
  //list is actually private but to keep things simple we make it public here
  std::list<Element_T> m_ls;
};

class MySortableElement
{
public:


  auto operator<=>(const MySortableElement& rhs) const;


  auto operator<=>(const std::shared_ptr<MySortableElement> rhs) const;
};


inline auto MySortableElement::operator<=>(const MySortableElement& rhs) const
{
  //just a dummy
  throw std::runtime_error("not implemented");
}

inline auto MySortableElement::operator<=>(const std::shared_ptr<MySortableElement> rhs) const
{
  //just a dummy
  throw std::runtime_error("not implemented");
}

Now if i invoke my class using

SortableList<MySortableElement> ls;

I'm getting an error that my concept is not satisfied. Do you know what I'm missing here? Godbolt Link: https://godbolt.org/z/c5nW71j7P

thx guys :)


Solution

  • First error is this:

    <source>(15): note: the concept 'Sortable_T<MySortableElement>' evaluated to false
    <source>(11): note: the concept 'std::same_as<void,std::strong_ordering>' evaluated to false
    C:/data/msvc/14.39.33321-Pre/include\concepts(35): note: 'void' and 'std::strong_ordering' are different types
    

    Your dummy implementations of operator<=> are too dummy. There is no return, so compiler deduces void as return type. That's part of the reason why I don't like the Almost Always Auto approach.

    Fix this by stating return type explicitly and/or implementing the operators in a way the compiler can deduce return type:

    inline std::strong_ordering MySortableElement::operator<=>(const MySortableElement& rhs) const
    {
      //just a dummy
      throw std::runtime_error("not implemented");
    }
    

    Then we see this error:

    <source>(12): note: cannot deduce template arguments for 'std::shared_ptr'
    

    std::shared_ptr cannot be created from an object. It's constructor would take a pointer to an object. But that constructor itself is a template, so it cannot deduce type of std::shared_ptr - you have to provide that explicitly.

      { a <=> std::shared_ptr<T>(&b) } -> std::same_as<std::strong_ordering>;
    

    But the simpler option would be to just provide an object of this kind in the context of concept:

    template<typename T>
    concept Sortable_T = requires(T a, T b, std::shared_ptr<T> sptr)
    {
      { a <=> b } -> std::same_as<std::strong_ordering>;
      { a <=> sptr } -> std::same_as<std::strong_ordering>;
    };