'Define a function signature containing a reference with C++20 Concepts

I am trying to use C++20 Concepts to constrain an interface. In this interface, I want a function signature to only use references. For some reason, I can't do this. Would someone help?

#include <concepts>

template <typename T>
concept MyInterface = requires(T t)
{
    // How do I specify a f(int&) instead of f(int) here?
    {t.f(int{})} -> std::integral;
};

struct X
{
    int f(int& i) { 
       return i; 
    }
};
static_assert(MyInterface<X>);


/*
** While tempting, this is _NOT_ a solution. **
template <typename T>
concept MyInterface = requires(T t, int& i) // Add a requirement here.
{
    {t.f(i)} -> std::integral;
};

// This will compile, despite allowing `i` to be an int. 
// When really, it should be a int&.
X::f(int i) { return i };
*/


Solution 1:[1]

I disagree with this comment, this is absolutely a solution (except you don't have to make i an int&, int suffices):

template <typename T>
concept MyInterface = requires(T t, int i)
{
    {t.f(i)} -> std::integral;
};

That is, indeed, your interface: you're passing an lvalue int into the member function f and you expect it to return some type which satisfies integral.


In order to reject callees taking int, you have to either pass some different type - like something convertible to int& instead of directly that:

template <typename T>
concept MyInterface = requires(T t, std::reference_wrapper<int> i)
{
    {t.f(i)} -> std::integral;
};

or then additionally reject the other case:

template <typename T>
concept MyInterface =
    requires(T t, int i) { {t.f(i)} -> std::integral; }
    && !requires(T t){ t.f(42); };

Passing reference_wrapper<int> is... ok. Explicitly rejecting taking int rvalues seems questionable at best.

Solution 2:[2]

You can utilize std::declval to create a reference type for the constraint:

template <typename T>
concept MyInterface = requires(T t)
{
    {t.f(std::declval<int&>())} -> std::integral;
};

Note that this would still allow T::f(int) to work. To disallow it, you can add another constraint to it:

template <typename T>
concept MyInterface = requires(T t)
{
    {t.f(std::declval<int&>())} -> std::integral;
}
&& !requires(T t)
{
    {t.f(std::declval<int>())} -> std::integral;
};

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Barry
Solution 2 Ranoiaetep