Вывод типа шаблона предупреждает о возвращении ссылки на локальный временный объект - PullRequest
0 голосов
/ 30 декабря 2018

Рассмотрим следующие функции: max:

template<typename T>
const T& max(const T& a, const T& b) {
    return b < a ? a : b;
}

// overload max(a, b) for char* arguments 
const char* max(const char* a, const char* b) {
    return std::strcmp(b, a) < 0 ? a : b;
}

template<typename T>
const T& max(const T& a, const T& b, const T& c) {
    return max(max(a,b), c);
}

Теперь при вызове трех аргументов max возвращается правильный результат.Тем не менее, он также показывает «Возвращение ссылки на локальный временный объект» во время компиляции.

int main() {
    const char* a = "aaa";
    const char* b = "bbb";
    const char* c = "ccc";

    auto result = max(a, b, c);
    // this correctly prints "ccc"
    std::cout << result << std::endl; 

    return 0;
}

Не могли бы вы шаг за шагом объяснить, как типы выводятся в этом примере, что приводит к возвращению локальной ссылки на временный объект?

Ответы [ 2 ]

0 голосов
/ 30 декабря 2018

In main:

auto result = max(a, b, c); вызывает третью перегрузку с T = const char*, поскольку она является единственной жизнеспособной (три аргумента).

В этой функции:

max(a,b) вызывает вторую перегрузку: для первой перегрузки T = const char* выбирается путем вычитания аргумента шаблона.Тогда все аргументы в перегрузке один и два считаются точным соответствием с целью перегрузки функции.Это связано с тем, что для первой перегрузки преобразование не требуется, а для второй перегрузки требуется только преобразование lvalue в rvalue.Поэтому ни одна из двух перегрузок не является предпочтительной из-за последовательности преобразования (не применяется ни одно из дополнительных правил упорядочения последовательностей преобразования), и вместо этого выбирается перегрузка два, потому что не шаблонные функции предпочтительнее шаблонных функций.

ДляДля внешнего вызова max(max(a,b),c) применимы те же рассуждения, что и выше, только то, что первый аргумент является выражением типа rvalue типа const char*, что делает ненужным преобразование lvalue-to-rvalue для второй перегрузки и для первой привязки ссылки на перегрузку считается точное совпадение .Снова ни одно из дополнительных правил упорядочения последовательности преобразования не применяется.

Следовательно, возвращаемое значение последнего выражения является временным const char*, которое будет связано со ссылкой, возвращаемой третьей перегрузкой.

0 голосов
/ 30 декабря 2018

Вы возвращаете char * по значению из своего среднего 2-параметра max () в три параметра max (), которые затем возвращают это временное значение по ссылке.

Вместо этого возьмите char * 's по ref, тоже.Вам также нужно потерять константы на вашем 3-х стороннем максимуме, чтобы вы не получили константные char * const's.Вдобавок ко всему, вам также нужно правильно обрабатывать значения r, что, я думаю, приведенный ниже код, по крайней мере, приближает к выполнению.Вы должны рассмотреть квалифицированный оператор r-значения <. </p>

#include <string.h>
#include <type_traits>
#include <utility>

struct C {
    bool operator<(C const & c) && {return false;}
};

// overload max(a, b) for char* arguments 
const char*& max(const char*& a, const char*& b) {
    return strcmp(b, a) < 0 ? a : b;
}

template<typename T>
decltype(auto) max(T&& a, T&& b) {
    return std::forward<T>(b) < std::forward<T>(a) ? std::forward<T>(a) : std::forward<T>(b);
}

template<typename T>
decltype(auto) max(T&& a, T&& b, T&& c) {
    return max(max(std::forward<T>(a), std::forward<T>(b)), std::forward<T>(c));
}

int main() {
    const char* a = "aaa";
    const char* b = "bbb";
    const char* c = "ccc";

    auto result = max(a, b, c);
    // this correctly prints "ccc"

    max(1, 2, 3);

    max("a", "b", "c");

    max (C(), C(), C());

    return 0;
}

https://godbolt.org/z/sMo9dx

...