В каких случаях компилятор создает новое временное локальное значение при сравнении двух типов? - PullRequest
2 голосов
/ 02 сентября 2011
template <typename T> inline T const& max (T const& a, T const& b) 
{ 
    return a < b ? b : a; 
}

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

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

int main () 
{ 
    const char* s1 = "frederic"; 
    const char* s2 = "anica"; 
    const char* s3 = "lucas"; 

    ::max(s1, s2, s3);
}  

Для приведенного выше кода книга (шаблоны C ++ - Уэсли) гласит:

Проблема в том, что если вы вызываете max () для трех C-строк, оператор возвращает max (max (a, b), c); становится ошибкой. Это связано с тем, что для C-строк max (a, b) создает новое временное локальное значение, которое может быть возвращено функцией по ссылке.

Теперь у меня вопрос: в каких случаях компилятор автоматически создает и возвращает временные переменные и почему (включая эту)?

Oh! Означает ли это, что компилятор создает временную переменную для хранения каждого значения, а затем возвращает эти сравниваемые временные переменные? Есть ли и другие случаи, в которых это делается?

Ответы [ 4 ]

2 голосов
/ 02 сентября 2011

Давайте сделаем это медленно:

::max(s1, s2, s3);

вызывает T const& max (T const& a, T const& b, T const& c) с T, являющимся char const*.

В этом методе мы имеем:

max(a,b)

который вызывает char const* max (char const* a, char const* b)

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

, таким образом, также char const* max (char const* a, char const* b)

И, следовательно, страшная часть (если мы переписываем):

template <typename T>
T const& max (T const& a, T const& b, T const& c)
{
  T result = max(max(a,b), c);
  return result;                 // reference to a temporary
}

Потому что T is char const*, а max(char const*, char const*) возвращает копию (указателя), а не постоянную ссылку на указатель.

gcc помогает диагностировать проблему (см. ideone ):

prog.cpp: In function ‘const T& max(const T&, const T&, const T&) [with T = const char*]’:
prog.cpp:27:   instantiated from here
prog.cpp:18: warning: returning reference to temporary

С 18 в качестве return max(....) в шаблоне и 27 в качестве вызова.

Если вы переписали max, чтобы "скрыть" это временное значение:

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

Вы помешаете обнаружению gcc (см. ideone ), но это не решит проблему.Clang предупредил бы, что T const& result связан с временным.

Реальная проблема в том, что char const* max(char const*, char const*) возвращает значение, поэтому либо все max должны возвращаться по значениям, либо все max должны принимать ивернуть константные ссылки ... если вы хотите их перепутать.

0 голосов
/ 02 сентября 2011

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

Теперь, если вы создадите специализацию:

template <> const char* max<const char*> (const char* a, const char* b);

Компилятор скажет вам, чтонесовместим с исходным шаблоном.

На самом деле правильная версия:

template <> const char * const & max<const char*> (const char* const &a, const char* const &b);

0 голосов
/ 02 сентября 2011

Это не имеет ничего общего с компилятором; это не один из тех вопросов о том, как компиляторы создают и оптимизируют временные значения, когда это зависит от реализации. То, что происходит, - это просто вопрос переменной области действия.

Вторая функция down возвращается по значению, то есть в памяти есть слот, предназначенный для хранения возвращаемого значения функции, и значение a или b копируется в него. Таким образом, он возвращает число, которое является адресом памяти, либо тот же адрес, что и в a, либо в b. Из этих двух строк один из адресов хранится дважды в памяти в двух разных местах.

Итак, внешний максимум в третьей функции: он будет возвращать по ссылке либо c (что безопасно), либо временный (что небезопасно). В этом случае временное расположение находится чуть выше вершины стека, и возвращение ссылки на него похоже на возврат указателя, в данном случае небезопасной памяти.

Либо вы можете очистить все целиком, чтобы использовать C-строки, если это идея, либо исправить функцию 2 для возврата безопасных ссылок.

0 голосов
/ 02 сентября 2011

Проблема в том, что перегрузка char const* max (char const* a, char const* b) возвращает значение, а не ссылку. Таким образом, временная переменная создается внутри template <typename T> inline T const& max (T const& a, T const& b, T const& c) и возвращается по ссылке, что плохо.

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

inline char const* const& max (char const* const& a, char const* const& b) 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...