Короткий вопрос: как заставить шаблонную функцию вести себя по-разному, основываясь на
на поддержку концепции параметров.
Я имею в виду, что если тип T реализует какую-то концепцию, то моя функция должна обрабатывать его специально, чем другие типы, которые этого не делают.
Расширение вопроса таково: как должна быть объявлена эта функция, чтобы конечный пользователь мог видеть, какие типы он поддерживает в своем объявлении (пользователь не должен проверять определение, чтобы получить эти знания).
Так что это мой конкретный пример, я использую идею «концепций», как она представлена в «Языке программирования C ++» Б. Страуструпом, а именно предикаты constexpr, вместо концепций c ++ 20, поскольку мой компилятор не поддержите, пока:
template<typename T>
bool constexpr is_matrix() { ... }
template<size_t N, size_t M, typename T>
class Matrix {
...
template<typename U>
Matrix& operator+=(const U& v) {
if constexpr (is_matrix<U>()) {
// handle matrix case
} else {
// handle scalar case
}
}
...
}
Этот пример взят из моей простой Matrix / Vector lib, которую я использую для изучения программного рендеринга. Моя идея здесь состоит в том, чтобы потребовать тип U , чтобы удовлетворить мою концепцию (поддерживать все необходимые операции, предоставить необходимые псевдонимы типов) вместо того, чтобы требовать, чтобы он был моим типом Matrix, в случае неудачи при проверке он должен обрабатываться как скаляр.
Итак, какие методы можно применить здесь, чтобы сделать этот код более понятным для конечного пользователя, и существуют ли более эффективные способы обеспечения концептуального параметрического полиморфизма, чем constexpr if?
Мое единственное решение этой проблемы, которое я смог придумать, было использование enable_if, например:
...
template<typename U, typename =
enable_if_t<is_convertible_v<U, T> ||
(is_matrix<U>() && is_convertible_v<matrix_value_type_t<U>, T>)>>
Matrix& operator+=(const U& v) {
...
Это довольно многословно и не может быть названо довольно, кстати, я бы предпочел статически утверждать, что тип должен быть либо конвертируемым, либо являться матрицей конвертируемых значений, а не скрывать этот оператор.
РЕДАКТИРОВАТЬ: После второго размышления о моем решении, я на самом деле мог static_assert в определении, но все еще предоставляя критерии утверждения в объявлении
template<typename U, bool check = is_convertible_v<U, T> || (is_matrix<U>() && is_convertible_v<matrix_value_type_t<U>, T>)>
... {
static_assert(check, "Type U should be either convertible to T, or being a matrix of convertible values");
}
EDIT2: что может быть улучшено до более читабельного варианта:
...
template <typename U, bool check = std::disjunction_v<
compatible_type<This, U>,
compatible_matrix<This, U>,
compatible_vector<This, U>>>
Matrix& operator+=(const U& v) {
assert_compatible<check>();
...