Висячие указатели / ссылки на константы типа int и char * - PullRequest
1 голос
/ 22 апреля 2019

Я читаю книгу о шаблонах C ++ от Vandevoorde, Josuttis и Gregor и не понимаю предупреждения, которое они делают о свисающих ссылках. Вот код:

#include <cstring>

// maximum of two values of any type (call-by-reference)
template<typename T>
T const& max (T const& a, T const& b)
{
  return  b < a ? a : b;
}

// maximum of two C-strings (call-by-value)
char const* max (char const* a, char const* b)
{
  return  std::strcmp(b,a) < 0  ? a : b;
}

// maximum of three values of any type (call-by-reference)
template<typename T>
T const& max (T const& a, T const& b, T const& c)
{
  return max (max(a,b), c);       // error if max(a,b) uses call-by-value
}

int main ()
{
  auto m1 = ::max(7, 42, 68);     // OK

  char const* s1 = "frederic";
  char const* s2 = "anica";
  char const* s3 = "lucas";
  auto m2 = ::max(s1, s2, s3);    // run-time ERROR
}

Данное сообщение заключается в том, что для C-строки вложенная max(a,b) создает висячую ссылку, а для int - нет. Так что же такого особенного в указателе на char по сравнению со ссылкой на int, учитывая, что оба они реализованы в виде указателей на объекты, расположенные вне max функций?

Ответы [ 2 ]

3 голосов
/ 22 апреля 2019

Это:

   char const* max (char const* a, char const* b)

возвращает значение временного указателя без имени, а затем это:

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

возвращает ссылку на него.

1 голос
/ 22 апреля 2019

... не понимаю предупреждение, которое они делают о свисающей ссылке

::max(s1, s2, s3) использует template<typename T> T const& max (T const& a, T const& b, T const& c), возвращая ссылку

Если определение template<typename T> T const& max (T const& a, T const& b, T const& c) будет изменено на:

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

, проблем не возникнет, поскольку у него уже есть ссылки.

Но с ::max(s1, s2, s3) T равно const char*, поэтому в max (max(a,b), c) max равно char const* max (char const* a, char const* b), чья не возвращает ссылку, из-за чего компилятор сохраняет результат char const* max (char const* a, char const* b) во временной переменной в стеке и возвращает ссылкук этой временной переменной, производящей ваше сообщение и связанную проблему.Это похоже на выполнение int & f() { int v = 0; return v; } за исключением того, что временная переменная создается самим компилятором.

Конечно, проблема исчезает с template<typename T> T const max (T const& a, T const& b, T const& c) (возвращая значение, а не ссылку), потому что значение возвращаетсяchar const* max (char const* a, char const* b) может быть возвращено напрямую.

Примечание для ::max(7, 42, 68) проблем нет, поскольку max в max (max(a,b), c) равно template<typename T> T const& max (T const& a, T const& b), что возвращает ссылку.

Чтобы продолжить возвращать ссылку в другом случае, вы можете специализировать max для char *, например:

// maximum of two C-strings (call-by-value)
template<>
char const* const & max (char const* const & a, char const* const & b)
{
  return  std::strcmp(b,a) < 0  ? a : b;
}

или определить его как

char const* const & max (char const* const & a, char const* const & b)
{
  return  std::strcmp(b,a) < 0  ? a : b;
}

, который возвращает ссылку версиис тремя параметрами можно использовать без необходимости использовать временную переменную и возвращать ссылку на нее.

(Лично я предпочитаю специализацию, потому что это кажется естественным иметь шаблонную версию)


#include <cstring>
#include <iostream>

// maximum of two values of any type (call-by-reference)
template<typename T>
T const& max (T const& a, T const& b)
{
  return  b < a ? a : b;
}

// MODIFIED
// maximum of two C-strings (call-by-value)
template<>
char const* const & max (char const* const & a, char const* const & b)
{
  return  std::strcmp(b,a) < 0  ? a : b;
}

// maximum of three values of any type (call-by-reference)
template<typename T>
T const& max (T const& a, T const& b, T const& c)
{
  return max (max(a,b), c);       // error if max(a,b) uses call-by-value
}

int main ()
{
  auto m1 = ::max(7, 42, 68);     // OK

  char const* s1 = "frederic";
  char const* s2 = "anica";
  char const* s3 = "lucas";
  auto m2 = ::max(s1, s2, s3);    // run-time ERROR

  std::cout << m2 << std::endl; // << ADDED TO CHECK
}

Компиляция и исполнение:

pi@raspberrypi:/tmp $ g++ -pedantic -Wall -Wextra s.cc
s.cc: In function ‘int main()’:
s.cc:28:8: warning: unused variable ‘m1’ [-Wunused-variable]
   auto m1 = ::max(7, 42, 68);     // OK
        ^~
pi@raspberrypi:/tmp $ ./a.out
lucas
...