Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is this a bug in the Mac OS X 10.6 C++ std API?

Tags:

c++

macos

The following code won't compile on Mac OS X 10.6; it gives an error in stl_algobase.h following a list of "instantiated from [...]" messages.

#include <vector>

int main( void )
{
    std::vector<int*> *v = new std::vector<int*>( 1, NULL );

    return 0;
}  

In order to make it compile, I have to place an explicit cast around NULL, i.e. replace it with (int*)(NULL).
That looks kind of awkward, though, and I doubt it should be really necessary; it also makes my source code look quite weird with structures and longer type names nested in namespaces, etc.

EDIT (Error message):

/Developer/SDKs/MacOSX10.6.sdk/usr/include/c++/4.2.1/bits/stl_algobase.h: In static member function 'static _OutputIterator std::__fill_n<true>::fill_n(_OutputIterator, _Size, const _Tp&) [with _OutputIterator = int**, _Size = int, _Tp = int]':
/Developer/SDKs/MacOSX10.6.sdk/usr/include/c++/4.2.1/bits/stl_algobase.h:665:   instantiated from '_OutputIterator std::fill_n(_OutputIterator, _Size, const _Tp&) [with _OutputIterator = int**, _Size = int, _Tp = int]'
/Developer/SDKs/MacOSX10.6.sdk/usr/include/c++/4.2.1/bits/stl_uninitialized.h:184:   instantiated from 'void std::__uninitialized_fill_n_aux(_ForwardIterator, _Size, const _Tp&, std::__true_type) [with _ForwardIterator = int**, _Size = int, _Tp = int]'
/Developer/SDKs/MacOSX10.6.sdk/usr/include/c++/4.2.1/bits/stl_uninitialized.h:219:   instantiated from 'void std::uninitialized_fill_n(_ForwardIterator, _Size, const _Tp&) [with _ForwardIterator = int**, _Size = int, _Tp = int]'  
/Developer/SDKs/MacOSX10.6.sdk/usr/include/c++/4.2.1/bits/stl_uninitialized.h:306:   instantiated from 'void std::__uninitialized_fill_n_a(_ForwardIterator, _Size, const _Tp&, std::allocator<_Tp2>) [with _ForwardIterator = int**, _Size = int, _Tp = int, _Tp2 = int*]'  
/Developer/SDKs/MacOSX10.6.sdk/usr/include/c++/4.2.1/bits/stl_vector.h:790:   instantiated from 'void std::vector<_Tp, _Alloc>::_M_initialize_dispatch(_Integer, _Integer, std::__true_type) [with _Integer = int, _Tp = int*, _Alloc = std::allocator<int*>]'  
/Developer/SDKs/MacOSX10.6.sdk/usr/include/c++/4.2.1/bits/stl_vector.h:261:   instantiated from 'std::vector<_Tp, _Alloc>::vector(_InputIterator, _InputIterator, const _Alloc&) [with _InputIterator = int, _Tp = int*, _Alloc = std::allocator<int*>]'  
/Users/JayZ/projects/C++/test/main.cpp:6:   instantiated from here
/Developer/SDKs/MacOSX10.6.sdk/usr/include/c++/4.2.1/bits/stl_algobase.h:641: error: invalid conversion from 'const int' to 'int*'  

If that could help...

like image 925
iolo Avatar asked Apr 09 '11 16:04

iolo


People also ask

What's the latest macOS update?

macOS 13: Ventura (Rome) – October 24, 2022 (Latest: 13.0)


1 Answers

What's happening is that the compiler has to select between one of the vector constructor overloads that allows more than one argument:

explicit vector(size_type n, const T& value = T(),
    const Allocator& = Allocator());

template <class InputIterator>
    vector(InputIterator first, InputIterator last,
        const Allocator& = Allocator());

The first overload (which you want to match) requires that the arguments be converted. The second requires no conversion, since the 2 arguments are the same type. So that's the overload the compiler is choosing. Unfortunately, the int type doesn't have all the operations that the second constructor uses for those arguments.

Forcing the arguments to be different types by casting NULL to int* or by making the first argument unsigned (1U) as other answers correctly suggest will avoid the problem by forcing the compiler to select the first constructor option.

Update:

When I compile this with MinGW 4.5.2 I get compile errors (different ones, but for the same reason, I believe). However, when I build with MSVC (2008 or 2010), I get no errors. When I look into the code that MSVC generates, it is actually matching the second overload (with 2 iterators) not the 'size and default value' overload.

"Aha", I think, "this is going to crash at runtime somewhere, because iterators with the value 1 and 0 don't make any sense".

However, when the 'iterator' types of that constructor happen to be int, the MSVC implementation performs a 'count/initial value' construction of the vector.

The standard says what that constructor must do when the InputIterator arguments of the constructor meet the requirements of an InputIterator. Since int doesn't meet the requirements of an InputIterator, the implementation has some free reign to do something else. The standard says that "An implementation can declare additional non-virtual member function signatures within a class" (17.4.4.4), and this would cover MSVC's behavior, I believe. The behavior that you're seeing and that I see with GCC 4.5.2, producing an error diagnostic, is also permitted by the standard.

I'm sure MSVC's behavior is what most users intend and want (until they move to the code to some other compiler maybe). And it's certainly clever.

like image 143
Michael Burr Avatar answered Oct 21 '22 22:10

Michael Burr