Код ниже перестал работать после последнего апдейта студии до версии 15.5.4.
Проверил на http://rextester.com VC еще компилирует, GCC нет. Видимо раньше VC был не прав.
Как правильно написать такую перегрузку?
#include <iostream>
template<typename L, typename R>
void (max)(L const& a, R const& b) {
std::cout << "void (max)(L const& a, R const& b)" << std::endl;
}
template<typename M>
void (max)(typename M::value_type scalar, M const& a) {
std::cout << "void (max)(typename M::value_type scalar, M const& a)" << std::endl;
}
template<typename T, typename U>
struct EmptyBase {
typedef T value_type;
typedef U this_type;
};
template<typename T>
struct Matrix : public EmptyBase<T, Matrix<T> > {
};
int main()
{
Matrix<double> m;
double s;
max(s, m);
std::cout << "Hello, world!\n";
}
p.s. вариант void (max)(double scalar, M const& a) не предлагать
Здравствуйте, Kazmerchuk Pavel, Вы писали:
KP>Код ниже перестал работать после последнего апдейта студии до версии 15.5.4. KP>Проверил на http://rextester.com VC еще компилирует, GCC нет. Видимо раньше VC был не прав. KP>Как правильно написать такую перегрузку? KP>p.s. вариант void (max)(double scalar, M const& a) не предлагать
Здравствуйте, Vamp, Вы писали:
V>Во-первых, зачем ты берешь имена функций в скобки?
С макросами борюсь )
V>Во-вторых, например вот так:
О, класс! То что надо
Почему исходный вариант не соответствует стандарту? Вроде он логичный и ожидаемое поведение (во всяком случае было у VC)
Здравствуйте, Kazmerchuk Pavel, Вы писали:
>Почему исходный вариант не соответствует стандарту? Вроде он логичный и ожидаемое поведение (во всяком случае было у VC)
Я не очень понимаю, почему VC это комилировал. У твоих функций одинаковый ранг — они обе шаблоны, обе неспециализированы. Не вижу причины почему один оверлоад должен предпочитаться.
KP>И еще вопрос. Такое можно совместить с автоматическим выводом типа возвращаемого значения?
Можно. Есть несколько способов. Самое прямое — засунуть enable_if_t в шаблонный аргумент вместо возвращаемого значения:
template<typename L, typename R, std::enable_if_t<!has_value_member<R>::value>* = nullptr>
auto max(L const& a, R const& b) {
std::cout << "void (max)(L const& a, R const& b)" << std::endl;
}
Здравствуйте, Vamp, Вы писали:
V>А зачем ты столько наворотил? Declaration перед definition, неиспользуемый третий дефолтный параметр шаблона? Вот так все отлично работает:
Это же не продакшен код, пример-то отрковенно синтетический. Третий параметр обозначал то, что специализации можно писать с использованием SFINAE. А Declaration перед definition обозначал, что определение primary template может отсутствовать в общем случае.
--
Не можешь достичь желаемого — пожелай достигнутого.
Здравствуйте, Vamp, Вы писали:
KP>>И еще вопрос. Такое можно совместить с автоматическим выводом типа возвращаемого значения? V>Можно. Есть несколько способов. Самое прямое — засунуть enable_if_t в шаблонный аргумент вместо возвращаемого значения:
V>
V>template<typename L, typename R, std::enable_if_t<!has_value_member<R>::value>* = nullptr>
V>auto max(L const& a, R const& b) {
V> std::cout << "void (max)(L const& a, R const& b)" << std::endl;
V>}
V>
Здравствуйте, rg45, Вы писали:
R>Здравствуйте, Kazmerchuk Pavel, Вы писали:
KP>>На перегрузке с двумя матрицами в качестве аргументов ломается...
R>https://ideone.com/vYcmFU
Нет! Ваш вариант был правильный! Для двух матриц должна первая перегрузка срабатывать void (max)(L const& a, R const& b).
Решение Vamp'а ломается.
Здравствуйте, Kazmerchuk Pavel, Вы писали:
KP>Нет! Ваш вариант был правильный! Для двух матриц должна первая перегрузка срабатывать void (max)(L const& a, R const& b). KP>Решение Vamp'а ломается.
Подход с реализацицией шаблонной функции через шаблон класса обладает двумя преимуществами:
1) В случаях, когда подходят одновременно и primary template, и какая-то из специализаций, не возникает коллизии, а просто отдается предпочтение специализации;
2) Этот подход зацищен от "сюрпризов" связанных с нежелательными неявными преобразованиями. Наприемер, если мы предоставляем обычную (возможно частичную) специализацию для какого-то конкретного типы или для семейства типов, порожденных от одного шаблона класса, то применяться такая специализация будет только для этих типов, отсекая возможные неявные преобразования. Если же требуется предоставить какую-то более широкую специализацию, например, для иерархии классов, или для типов, допускающих неявные преобразования, в нашем распоряжении SFINAE, is_base_of, is_convertilbe и целый арсенал других стандартных и рукописных метафункций.
В результате этот подход, несмотря на кажущуюся, на первый взгляд, громоздкость, на практике оказывается гораздо более еффективным. И чем больше специальных случаев, тем больше этот эффект ощущуается. Подход же с перегрузками, при расширеннии, скатывается в какой-то ад и, в конце-концов, приводит к желанию "переписать все нахрен" (с). Вот почему предложенный мной подход я склонен рассматривать как стандартный.
--
Не можешь достичь желаемого — пожелай достигнутого.
KP>Нет! Ваш вариант был правильный! Для двух матриц должна первая перегрузка срабатывать void (max)(L const& a, R const& b). KP>Решение Vamp'а ломается.
У меня нет никакого решения, потому что я не знаю задачи. Я просто показал, как можно включать и выключать перегрузки для приведенного примера. Правильное ли это **решение** в твоем случае? Понятия не имею. На первый взгляд кажется, что вообще нет, потому что непонятно нафиг специализировать max, если можно просто определить оператор > для матрицы? Наверное, у тебя есть свои причины, но мне они не известны.