LINUX.ORG.RU

Неявное преобразование шаблонных структур

 


0

4

Допустим, у меня есть какой-то такой код:

#include <iostream>

struct foo {
    int n;
    foo(int n) : n(n) {}
};

template <class T>
struct bar {
    T n;
    bar(T n) : n(n) {}
};

foo operator * (foo u, foo v) {
    return foo(u.n * v.n);
}

template <class T>
bar<T> operator * (bar<T> u, bar<T> v) {
    return bar<T>(u.n * v.n);
}

int main() {
    foo x = 1;
    std::cout << (x * 2).n << std::endl;
    bar<int> y = 2;
    std::cout << (y * 3).n << std::endl;
}

Часть с foo работает, а часть с bar — нет. Что я делаю не так, и можно ли вообще как-то сделать оператор, который будет делать implicit cast одного из аргументов к bar<T> в случае, если другой уже им является?

★★★

Добавь

template<class T, class U>
bar<T> operator*(U u, bar<T> v)
{
  return bar<T>(u) * v;
}

template<class T, class U>
bar<T> operator*(bar<T> v, U u)
{
  return v * bar<T>(u)
}

azelipupenko ()

Clang ответил на твой вопрос: https://wandbox.org/permlink/kjCtnbp6podnFmU4

Ну, и еще надо прочитать правила дедукции агрументов. Что-то мне подсказывает что в bar<T> уже не так просто с дедукцией T

KennyMinigun ★★★★★ ()
Последнее исправление: KennyMinigun (всего исправлений: 1)
Ответ на: комментарий от azelipupenko

Понятно, что можно так сделать. Меня скорее интересует, почему не работает так же, как с foo.

f1u77y ★★★ ()
Ответ на: комментарий от KennyMinigun

clang

Там написано gcc, но видимо не важно.

ответил

Нет, я уже пытался вчитываться в сообщения компилятора. Понял, что неявного каста из int в bar<int> не существует. Не понял, почему его не существует, хотя аналогичный каст для нешаблонного класса есть.

f1u77y ★★★ ()
Ответ на: комментарий от f1u77y

Не понял, почему его не существует, хотя аналогичный каст для нешаблонного класса есть.

Потому что шаблон класса - это ещё не класс. Классом он становится при инстанцировании. Запись (y * 3) не приводит к инстанцированию класса bar<int> (и не должна, потому что ты уже указал тип второго операнда - int), а приводит к поиску перегрузки operator*(bar<int>, int) по правилам перегрузки.

azelipupenko ()
Ответ на: комментарий от azelipupenko

Запись (y * 3) не приводит к инстанцированию класса bar<int>

К инстанцированию приводит запись bar<int> y

tailgunner ★★★★★ ()
Ответ на: комментарий от f1u77y

Понял, что неявного каста из int в bar<int> не существует. Не понял, почему его не существует, хотя аналогичный каст для нешаблонного класса есть.

Именно потому, что он шаблонный? https://stackoverflow.com/questions/9787593/c-implicit-type-conversion-with-t...

tailgunner ★★★★★ ()
Ответ на: комментарий от f1u77y

Там написано gcc, но видимо не важно.

Еее, ссылкой промахнулся. Но и действительно, gcc хорошо разьяснил.

KennyMinigun ★★★★★ ()
Ответ на: комментарий от f1u77y

Понял, что неявного каста из int в bar<int> не существует.

Он существует. В твоем случае компилятор просто не может найти нужный оператор но при этом не пытается делать неявный каст.

KennyMinigun ★★★★★ ()
Ответ на: комментарий от tailgunner

К инстанцированию приводит запись bar<int> y

Да, ты прав. Но эта запись приводит к инстанцированию operator*(bar<int>, int), а не (bar<int>, bar<int>). Отсюда и проблема.

azelipupenko ()
Ответ на: комментарий от azelipupenko

Но эта запись приводит к инстанцированию поиску operator*(bar<int>, int)

:)

KennyMinigun ★★★★★ ()
Ответ на: комментарий от azelipupenko

Проблема, насколько я понимаю, в том, что для обычного и шаблонного классов преобразования ищутся по-разному.

tailgunner ★★★★★ ()
Ответ на: комментарий от tailgunner

В том ответе же не так написано. Проблема в том, что второй оператор является шаблоном, а для аргументов шаблона не выполняются неявные преобразования. Если первый оператор переделать в:

template <class T>
T operator * (T u, T v) {
    return T(u.n * v.n);
}
То строка с foo тоже не будет работать.

xaizek ★★★★★ ()
Ответ на: комментарий от xaizek

Если первый оператор переделать в:

template <class T>

T operator * (T u, T v) {

Какой «первый» - перемножения foo?

tailgunner ★★★★★ ()
Ответ на: комментарий от tailgunner

Проблема, насколько я понимаю, в том, что для обычного и шаблонного классов преобразования ищутся по-разному.

Если быть точнее, то дело обстоит так. В месте вызова функции компилятор ищет кандидатов для разрешения перегрузки. Для вызова operator*(y, 3) он ищет operator*(bar<int>, int) и не находит. Компилятор не рассматривает operator*(bar<T>, bar<T>) как кандидата потому что дедукция для второго аргумента оказывается невозможной. А невозможной она оказывается потому, что компилятор не выполняет неявных преобразований для аргументов, зависящих от параметра шаблона, что отражено стандартом в 14.8.1.6:

Implicit conversions (Clause 4) will be performed on a function argument to convert it to the type of the corresponding function parameter if the parameter type contains no template-parameters that participate in template argument deduction. [Note: Template parameters do not participate in template argument deduction if they are explicitly specified.

Т.е. если указать параметр шаблона явно, то тогда компилятору не нужно заниматься дедукцией аргументов, и он вызовет неявные преобразования как и для аргументов обычной функции:

  bar<int> y{2};
  std::cout << operator*<int>(y, 3).n << std::endl; // ok

Лол :-)

azelipupenko ()

Можно сделать так

template <class T>
struct bar {
    T n;
    bar(T n) : n(n) {}

    friend bar operator * (bar u, bar v) {
        return bar (u.n * v.n);
    }
};
anonymous ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.