Предупреждение о переполнении стека во время выполнения Visual Studio 2008, когда оператор‹ рекурсивен на всех путях

Приведенный ниже код C++ генерирует следующее предупреждение в Visual Studio 2008:

1>c:...\sample.cpp(6): предупреждение C4717: 'оператор‹': рекурсивный на всех путях управления, функция > вызовет переполнение стека во время выполнения

Если я использую класс Sample в любой ситуации, когда требуется оператор ‹, он фактически падает с ошибкой переполнения стека (например, после вставки второго объекта Sample в мультимножество). Конструктор продолжает вызываться до тех пор, пока не закончится место в стеке.

Приведенный ниже код — это все, что нужно для генерации предупреждения (без каких-либо ссылок в коде на класс Sample).

// Sample.hpp
#include <iostream>

class __declspec(dllexport) Sample
{
  std::string ID;
public:
  Sample (std::string id):ID(id) {};
  friend bool __declspec(dllexport) operator<(const Sample& s1, const Sample& s2);
};


// Sample.cpp
#include "Sample.hpp"

bool operator<(const Sample& s1, const Sample& s2)
{
  return s1.ID<s2.ID;
}

Предупреждение отображается с VC++ и VS2008 (Win32, /W3) на Win7. Для той же платформы и точно такого же кода, но с MinGW GCC 4.7.3 в eclipse я не получаю никаких предупреждений.

Если я добавлю заголовок ‹ string >, предупреждение исчезнет в VS2008, и любое использование класса Sample будет работать отлично.

Кроме того, если я объявлю конструктор Sample явным, VS2008 выдаст следующую ошибку компиляции:

1>.\Sample.cpp(5): ошибка C2678: двоичный файл '‹': не найден оператор, который принимает левый операнд > типа 'const std::string' (или нет приемлемого преобразования) 1> c :...\Sample.hpp(13): может быть 'логическим оператором ‹(const Sample &,const Sample &)' 1> при попытке сопоставить список аргументов '(const std::string, const std::string )'

Однако установка явного конструктора в GCC по-прежнему не генерирует ни предупреждений, ни ошибок (я установил предупреждения в Eclipse на наиболее полные уровни, какие только мог).

Я хотел бы знать, может ли кто-нибудь примерно объяснить, как VS2008 определяет, когда генерировать это предупреждение о переполнении стека. В этом случае это оказывается правильным, поэтому мне любопытно посмотреть, как это делается. Также, если возможно, почему GCC ведет себя по-другому здесь, пожалуйста. Надеюсь, это имеет смысл.


person koppa    schedule 04.08.2013    source источник
comment
Вам обязательно нужно включить <string>, чтобы иметь возможность сравнивать std::string объекты. Не уверен, что это решает ВСЕ проблемы, но должно решить вторую.   -  person Mats Petersson    schedule 04.08.2013
comment
Из сообщений об ошибках видно, что VC++ выполняет сравнение, создавая объект Sample из каждой сравниваемой строки, а затем сравнивая их. Вот почему явный ctor его ломает и почему он бесконечно рекурсивен — потому что он создает новые временные объекты того же типа, что и полученные в качестве параметров, и пытается их сравнить.   -  person Jerry Coffin    schedule 04.08.2013
comment
@Матс Спасибо. У меня есть изрядное количество устаревшего кода, о котором нужно позаботиться. Когда я добавил этот класс в набор контейнеров, я получил ошибку переполнения стека. По крайней мере, это был очевидный сбой, а не неприятная ошибка с утечкой. Каким-то образом кодировщик забыл включить ‹ string ›, вероятно, потому, что он все равно будет нормально компилироваться. Не помогло и то, что около десятка предупреждений было вручную отключено в файле проекта.   -  person koppa    schedule 04.08.2013


Ответы (1)


Это происходит потому, что в момент сравнения std::string является неполным типом, а конструктор преобразования Sample (std::string id) вызывается неявно. В выражении s1.ID<s2.ID и левая, и правая стороны неявно преобразуются во временную Sample, а оператор преобразования затем вызывается снова (и снова, и снова).

Вам нужно включить <string>, чтобы было видно полное определение std::string, и я настоятельно рекомендую объявить конструктор примера explicit.

person Captain Obvlious    schedule 04.08.2013
comment
Понятно, понятно спасибо. Все еще удивляюсь, как это случилось, vc++ обнаружил и предупредил о надвигающемся переполнении стека, тогда как gcc этого не сделал. Из вашего объяснения кажется, что тот факт, что оператор преобразования вызывается снова и снова, не зависит от реализации. - person koppa; 04.08.2013
comment
Компилятор не обязан выдавать предупреждение о рекурсии. Проверьте параметры компиляции GCC и поднимите уровни предупреждений с помощью -Wall -Wextra -Wstrict-aliasing -Wstrict-overflow, чтобы увидеть, перехватывает ли он переполнение. Вы правы в том, что преобразование не зависит от реализации и четко определено стандартом (начните с 4.2 [conv]). В качестве примечания: если бы оператор сравнения был членом Sample, неявное преобразование не произошло бы. - person Captain Obvlious; 04.08.2013