Несовместимость между вектором / алгоритмом и iostream - PullRequest
0 голосов
/ 15 апреля 2019

В старых версиях компилятора Borland C ++, похоже, возникла проблема с использованием корректной перегрузки std :: remove, когда было включено <iostream>.

Чтобы воспроизвести эту ошибку, вам нужна устаревшая версия Borland C ++Builder (например, C ++ Builder 6) и этот очень маленький фрагмент кода:

#include <vector>
#include <algorithm>

void __fastcall TFormMain::Button1Click(TObject *Sender)
{
    std::vector< int > Selection;
    std::remove( Selection.begin(), Selection.end(), 10 );
}

(я знаю: этот код ничего не делает, но по крайней мере компилирует ...)

Все работает отличнопока вы не включите iostream где-нибудь в коде:

#include <vector>
#include <iostream>
#include <algorithm>

void __fastcall TFormMain::Button1Click(TObject *Sender)
{
    std::vector< int > Selection;
    std::remove( Selection.begin(), Selection.end(), 10 );
}

Это вызовет некоторые ошибки компилятора:

[C++ Fehler] UnitFormMain.cpp(22): E2034 Konvertierung von 'int *' nach 'const char *' nicht möglich
[C++ Fehler] UnitFormMain.cpp(22): E2342 Keine Übereinstimmung des Typs beim Parameter '__path' ('const char *' erwartet, 'int *' erhalten)
[C++ Fehler] UnitFormMain.cpp(22): E2227 Zu viele Parameter im Aufruf von std::remove(const char *)

На английском языке:

[C++ Error] UnitFormMain.cpp(22): E2034 Cannot convert 'int *' to 'const char *'
[C++ Error] UnitFormMain.cpp(22): E2342 Type mismatch in parameter '__path' (wanted 'const char *', got 'int *')
[C++ Error] UnitFormMain.cpp(22): E2227 Extra parameter in call to std::remove(const char *)

Мой вопрос:

Есть ли какой-нибудь элегантный / правильный способ исправить эту проблему, не разделяя код на разные файлы?

Ответы [ 2 ]

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

BCB6 имеет две библиотеки STL - STLPort и RogueWave. STLPort используется по умолчанию, RogueWave предоставляется для обратной совместимости с предыдущими версиями BCB.

Ваш код пытается вызвать функцию STL std::remove() из заголовка <algorithm> STLPort (он определен в <stl/_algo.h>):

template <class _ForwardIter, class _Tp>
_STLP_INLINE_LOOP _ForwardIter 
remove(_ForwardIter __first, _ForwardIter __last, const _Tp& __value)

Однако библиотека времени выполнения C имеет собственную функцию с одним параметром remove() в заголовке <stdio.h>:

int       _RTLENTRY _EXPFUNC remove(const char * __path);

Эта функция C заносится в пространство имен std в C ++ с помощью заголовка <cstdio>, который включает в себя заголовок <algorithm> STLPort. Есть даже комментарий о remove() в точке, где <algorithm> включает <cstdio> до <stl/_algo.h>:

# if ! defined (_STLP_USE_NAMESPACES)
// remove() conflicts, <cstdio> should always go first
#  include <cstdio>
# endif

# ifndef _STLP_INTERNAL_ALGO_H
#  include <stl/_algo.h>
# endif

И даже _algo.h имеет аналогичный комментарий:

# ifdef __SUNPRO_CC
// remove() conflict
#  include <cstdio>
# endif

Таким образом, STLPort всегда включает <cstdio> перед определением собственного алгоритма remove(), , предположительно для разрешения конфликта имен.

Но, как говорится, все ошибки, которые вы видите, связаны с тем, что компилятор думает, что вы пытаетесь вызвать 1-параметрическую функцию std::remove() C, а не 3-параметрическую std::remove() функцию STL. Почему компилятор так думает, я не знаю. Вероятно, это ошибка компилятора в том, как BCB6 разрешает перегрузки.

Однако проблема касается только STLPort, а не RogueWave, поскольку заголовок <algorithm> в RogeWave не приводит к включению <cstdio> (на самом деле, RogueWave даже не пытается обойти любой конфликт имен с remove() между C и STL, как это делает STLPort).

Итак, одним из решений является включение использования RogueWave вместо STLPort путем определения _USE_OLD_RW_STL перед любым из заголовков STL:

#define _USE_OLD_RW_STL
// alternatively, add `_USE_OLD_RW_STL` to the Conditionals
// list in the Project Options...

#include <vector>
#include <iostream>
#include <algorithm>

...

std::vector< int > Selection;
std::remove( Selection.begin(), Selection.end(), 10 ); // WORKS

В противном случае, если вы хотите использовать STLPort, вы можете использовать предложение, упомянутое Камилом Цуком в комментариях:

#include <vector>

#define remove _mask_remove
#include <iostream>
#undef remove

#include <algorithm>

...

std::vector< int > Selection;
std::remove( Selection.begin(), Selection.end(), 10 ); // WORKS

Или используйте ответ , предложенный StoryTeller :

#include <vector>
#include <algorithm>

namespace resolve_std {
    using std::remove;
}

#include <iostream>

...

std::vector< int > Selection;
resolve_std::remove( Selection.begin(), Selection.end(), 10 ); // WORKS

Я протестировал все эти решения в BCB6, все они работают в этом сценарии.

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

Есть кое-что, что вы можете попробовать, но возьмите это с крошкой соли, так как у меня нет C ++ builder, доступного для тестирования этого.

Объявление using может вводить имена из одного пространства имен в другое, что может использоваться в квалифицированном поиске. Однако будут введены только перегрузки, которые видны в точке объявления использования. Поэтому, если мы получаем только те перегрузки, которые мы хотим , и проводим квалифицированный поиск имен в новом пространстве имен, не должно быть двусмысленности. Вот это в коде:

#include <vector>
#include <algorithm>

namespace resolve_std {
    using std::remove;
}

#include <iostream>

void __fastcall TFormMain::Button1Click(TObject *Sender)
{
    std::vector< int > Selection;
    // Should only use the overloads introduced prior to including <iostream>
    resolve_std::remove( Selection.begin(), Selection.end(), 10 );
}
...