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.