8

Possible Duplicate:
Using array as map value: Cant see the error

Assume I have the following data structure:

std::map<size_t, double[2] > trace;

how can I access its elements with the operator[]?

Essentially I want to do something like:

trace[0][0] = 10.0;
trace[0][1] = 11.0;

In compiling these lines of code I get the following error:

/usr/include/c++/4.6/bits/stl_map.h:453:11: error: conversion from ‘int’ to non-scalar type ‘std::map<long unsigned int, double [2]>::mapped_type {aka double [2]}’ requested

comments?

Community
  • 1
  • 1
arthur
  • 1,034
  • 2
  • 17
  • 31
  • You have an array of double in the map. The map needs a key to store/bind a value to. How did you assign values to the map? – Mike de Klerk Nov 23 '12 at 10:54
  • 1
    `std::map` is that legal? I didn't think you could use arrays in the STL like that. – john Nov 23 '12 at 10:56
  • 2
    this post might help you http://stackoverflow.com/questions/2582529/using-array-as-map-value-cant-see-the-error – denizeren Nov 23 '12 at 10:57
  • 1
    @SingerOfTheFall: because arrays are neither copyable nor movable. – Yakov Galka Nov 23 '12 at 10:59
  • @SingerOfTheFall If I knew that I wouldn't be asking the question. – john Nov 23 '12 at 10:59
  • 3
    consider using `std::map>` instead (if you have that possibility, else `std::map >` – Nim Nov 23 '12 at 10:59
  • @ybunga, sorry, I misinterpreted the OP, for some reason I thought there was a pointer inside ;) – SingerOfTheFall Nov 23 '12 at 11:00
  • 2
    this is not legal. A c-style array is not copy constructable and therefore can't be used in as a map value. What compiler are you using? gcc barks about `ISO C++ forbids casting to an array type double[2]` –  Nov 23 '12 at 11:01
  • 2
    @ybungalobill: But I think they are default-constructible, which is all that's required of them here. They'd only need to be copy- or move-constructible if you wanted to use `insert`. – Mike Seymour Nov 23 '12 at 11:04
  • 1
    compiler: g++ --version g++ (Ubuntu/Linaro 4.6.1-9ubuntu3) 4.6.1 Copyright (C) 2011 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. – arthur Nov 23 '12 at 11:07
  • strangely enough c++ compiler did not complain about std::map – arthur Nov 23 '12 at 11:12
  • @Mike Seymour is it what exactly happening here? when I reference trace[0], c++ invokes the insert and starts complaining? – arthur Nov 23 '12 at 11:13
  • @Mike de Klerk: I assigned the keys with trace[0]. The key in this case is 0, or did I misunderstand your question? – arthur Nov 23 '12 at 11:15
  • @user8723: Actually I was wrong - you can't use an array, because the language mysteriously forbids you from explicitly value-initialising them, which is required for map's value types. – Mike Seymour Nov 23 '12 at 11:24

3 Answers3

5

Value types for maps must be default-constructible, using an expression value_type(), in order to access them via []. For some mysterious reason, array types are not, as specified in C++11 5.3.2/2 (with my emphasis):

The expression T(), where T is a simple-type-specifier or typename-specifier for a non-array complete object type ... creates a prvalue of the specified type,which is value-initialized

The compiler gives that strange error when trying to value-initialise an array; the following gives the same error:

typedef double array[2];
array();

I suggest using a class type rather than double[2]: std::array<double,2>, std::pair<double,double>, or a struct containing two doubles. Such types are copyable, don't decay into a pointer (losing compile-time knowledge of the size) at the drop of a hat, and are generally easier to deal than a built-in array type.

If you're stuck with an old library that doesn't provide std::array, then you could use the very similar Boost.Array, or write your own simple class template to wrap an array.

Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
1

You can wrap an array:

template<typename T, unsigned int n>
struct ArrayWrapper
{
    T v[n];
    T& operator[](unsigned int i) { return v[i]; } // You can also check for out-of-bounds errors
    const T& operator[](unsigned int i) const { return v[i]; } // You can also check for out-of-bounds errors
};
#include <map>
#include <iostream>
int main()
{
    typedef std::map<size_t, ArrayWrapper<double,2> > Map;
    Map trace;
    trace[1][0] = 42;
    for(Map::const_iterator it = trace.begin(); it != trace.end(); ++it)
        std::cout << "( " << (*it).first
            << ", " << (*it).second[0]
            << ", " << (*it).second[1]
            << ")\n";
}

Live example. Else, if you have C++11, you should use std::array; if not, and you have Boost, you can use Boost.Array.

Synxis
  • 9,236
  • 2
  • 42
  • 64
1

Your code compiles till:

std::map<size_t, double[2]> trace;

because double[2] is a valid type. You can declare a template class object with any valid type. For example:

template<typename T>
struct X {
  // T t; // this will complain for sure
  void foo () { T t; } // this won't complain unless invoked !!
};
X<void> x;

will compile without any problem.
If you call x.foo(), the compiler will complain about it.

Same thing is happening in case of std::map. When you invoke functions like map::insert(), map::operator [] where the value-type double[2] is actually going to be used, the compiler will start complaining as double[2] is not copyable (exception: I have seen an old g++3.x version where arrays were assignable and copyable).

You can encapsulate your array into a structure.

struct D { double d[2]; };

If you want to avoid using raw array then std::array is good option as mentioned in comments and other answer.

iammilind
  • 68,093
  • 33
  • 169
  • 336