Home > Blockchain >  C concept: check if a method/operator exists no matter what return type is
C concept: check if a method/operator exists no matter what return type is

Time:03-05

Suppose I am writing a template function that wants to call operator = on its template parameter and does not care whether it returns the new value (of type T), a void, or whatever else. For example:

template<class T>
void incrementAll(T *array, int size, T value) {
    for (int i = 0; i < size;   i) {
        array[i]  = value;
    }
}

I want to have a constraint on this function: to make it require a concept on T. However, I couldn't find one that does not have any requirement on the return type. The only idea I could come up with was std::convertible_to<void> (because we can convert any value to void, right?), but it fails:

#include <concepts>

template<class T>
concept Incrementable = requires(T a, T b) {
        {a  = b} -> std::convertible_to<void>;
};

template<class T> requires Incrementable<T>
void incrementAll(T *array, int size, T value) {
    for (int i = 0; i < size;   i) {
        array[i]  = value;
    }   
}

int main() {
        int arr[] = {1};
        incrementAll(arr, 1, 1); 
}
nikolay@KoLin:~$ g   another_tmp.cpp -std=c  20 -fconcepts-diagnostics-depth=10
another_tmp.cpp: In function ‘int main()’:
another_tmp.cpp:17:21: error: no matching function for call to ‘incrementAll(int [1], int, int)’
   17 |         incrementAll(arr, 1, 1);
      |         ~~~~~~~~~~~~^~~~~~~~~~~
another_tmp.cpp:9:6: note: candidate: ‘template<class T>  requires  Incrementable<T> void incrementAll(T*, int, T)’
    9 | void incrementAll(T *array, int size, T value) {
      |      ^~~~~~~~~~~~
another_tmp.cpp:9:6: note:   template argument deduction/substitution failed:
another_tmp.cpp:9:6: note: constraints not satisfied
another_tmp.cpp: In substitution of ‘template<class T>  requires  Incrementable<T> void incrementAll(T*, int, T) [with T = int]’:
another_tmp.cpp:17:14:   required from here
another_tmp.cpp:4:9:   required for the satisfaction of ‘Incrementable<T>’ [with T = int]
another_tmp.cpp:4:25:   in requirements with ‘T a’, ‘T b’ [with T = int]
another_tmp.cpp:5:12: note: ‘a  = b’ does not satisfy return-type-requirement, because
    5 |         {a  = b} -> std::convertible_to<void>;
      |          ~~^~~~
another_tmp.cpp:5:10: error: deduced expression type does not satisfy placeholder constraints
    5 |         {a  = b} -> std::convertible_to<void>;
      |         ~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
another_tmp.cpp:5:10: note: constraints not satisfied
In file included from another_tmp.cpp:1:
/usr/include/c  /11.2.0/concepts:72:13:   required for the satisfaction of ‘convertible_to<decltype(auto) [requires std::convertible_to<<placeholder>, void>], void>’ [with decltype(auto) [requires std::convertible_to<<placeholder>, void>] = int&]
/usr/include/c  /11.2.0/concepts:72:30: note: the expression ‘is_convertible_v<_From, _To> [with _From = int&; _To = void]’ evaluated to ‘false’
   72 |     concept convertible_to = is_convertible_v<_From, _To>
      |                              ^~~~~~~~~~~~~~~~~~~~~~~~~~~~

So, what is the correct concept to define here?

CodePudding user response:

If you don't care what its return type is, then you can simply use the requires-clause to require the expression a = b to be well-formed:

template<class T>
concept Incrementable = requires(T a, T b) {
  a  = b;
};

However, such a concept seems too loose for the incrementable type, and it seems more appropriate to require the return type to be T& like {a = b} -> same_as<T&> and name it AdditionAssignable or something.

  • Related