c++c++builder-6

Incompatibility between vector/algorithm and iostream


Older versions of the Borland C++ compiler seem to have a problem using the correct overload of std::remove when <iostream> has been included.

To reproduce this error you need an elderly version of Borland C++ Builder (e.g. C++ Builder 6) and this very small code snippet:

#include <vector>
#include <algorithm>

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

(I know: this code does nothing, but at least it compiles...)

Everything works fine until you include iostream somewhere in the code:

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

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

This will cause some compiler errors:

[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 *)

In English:

[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 *)

My question is:

Is there any elegant/correct way to fix this issue without separating the code into different files?


Solution

  • BCB6 has two STL libraries - STLPort and RogueWave. STLPort is the default, RogueWave is provided for backwards compatibility with previous BCB versions.

    Your code is trying to call the STL std::remove() function from the <algorithm> header of STLPort (it is actually defined in <stl/_algo.h>):

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

    However, the C runtime library has a single-parameter remove() function of its own, in the <stdio.h> header:

    int       _RTLENTRY _EXPFUNC remove(const char * __path);
    

    This C function is brought into the std namespace in C++ by the <cstdio> header, which STLPort's <algorithm> header includes. There is even a comment about remove() at the point where <algorithm> includes <cstdio> before <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
    

    And even _algo.h has a similar comment:

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

    So, STLPort always includes <cstdio> before defining its own remove() algorithm, supposedly to deal with the naming conflict.

    But, that being said, all of the errors you are seeing are due to the compiler thinking you are trying to call the 1-parameter std::remove() C function rather than the 3-parameter std::remove() STL function. Why the compiler thinks that, I do not know. It is probably a compiler bug in how BCB6 resolves overloads.

    However, the issue only affects STLPort, not RogueWave, because RogeWave's <algorithm> header does not cause <cstdio> to be included (in fact, RogueWave doesn't even try to work around any naming conflict with remove() between C and STL, like STLPort does).

    So, one solution is to enable the use of RogueWave instead of STLPort, by defining _USE_OLD_RW_STL before any of the STL headers:

    #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
    

    Otherwise, if you want to use STLPort, you can use the suggestion mentioned by Kamil Cuk in comments:

    #include <vector>
    
    #define remove _mask_remove
    #include <iostream>
    #undef remove
    
    #include <algorithm>
    
    ...
    
    std::vector< int > Selection;
    std::remove( Selection.begin(), Selection.end(), 10 ); // WORKS
    

    Or, use the answer proposed by 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
    

    I have tested all of these solutions in BCB6, they all work in this scenario.