user3842086
user3842086

Reputation: 15

C++ arrays and System::Single?

I'm relatively new to C++, and I'm trying to take an array within a class, and set it equal to a passed in array.

public ref class Example {
    array<float> ^ myarray1 = gcnew array<float>(3);
public:
    Example(float^ myarray2) {
        int i = 0;
        while (i<3) {
            myarray[i] = myarray2[i];
            i += 1;
        }
}

In the main function, the constructor is called as follows:

float myarray2[] = {1,2,3};
Example ^example1 = gcnew Example(*myarray2)

The errors I get is are as follows:

  1. System::Single' has no default indexed property (class indexer)
  2. expression must have pointer-to-object or handle-to-C++/CLI-array type

Both of these errors are identified as happening where I am saying myarray[i] = myarray2[i].

I would greatly appreciate any help with solving this problem. I can't see where or how System::Single is getting pulled in as an error message. And, before it is suggested, I know I can get to work with setting myarray2 as a array float like myarray1, but I want it to work passing in myarray2 as float^ myarray2.

Upvotes: 0

Views: 478

Answers (2)

Theodoros Chatzigiannakis
Theodoros Chatzigiannakis

Reputation: 29213

Since you say you're new to C++, let me point out that you're not writing classic C++ there. You're writing C++/CLI, which is a set of language extensions to C++ designed to interoperate with the CLI (.NET Framework). Because of this, the type float in your code is an alias for the type System::Single of the framework.

Regarding the indexer issue, the error messages pretty much spell out the cases in which you would be allowed to use an indexer:

  • System::Single' has no default indexed property (class indexer)

    You could use an indexer if the type had a defined indexed property. System::Single, also known as float, doesn't happen to have one.

  • expression must have pointer-to-object type

    You could use the indexer if the type was a non-void pointer type. You'd have to declare it like this:

    Example(float* myarray2) {
    

    In this case, myarray2[i] is equivalent to the expression *(myarray2 + i).

  • or handle-to-C++/CLI-array type

    You could use the indexer if the type was a handle (^) to a C++/CLI array type. As you already know, you'd have to declare it like this:

    Example(array<float> ^ myarray2) {
    

The bottom line is that, although you can treat a float* (pointer to float) like a C-style array of float (as a result of the rules of C and C++ about arrays and pointer arithmetic), these things simply do not apply to the float^ (handle to float) type (which is C++/CLI-specific).

Upvotes: 3

Hans Passant
Hans Passant

Reputation: 941585

   Example(float^ myarray2)

That does not mean what you think it does. You are used to C language behavior, a float[] can automatically decay to a float* to the first element of the array. Somewhat unfortunately also carried forward into C++.

But not into C++/CLI, it is fundamentally unverifiable code. And responsible for a very large number of bugs and security problems. One core problem is that your constructor has no idea how many elements are stored in the array. You hard-coded "3" but if the caller passes an array that's smaller then Very Bad Things happen.

What it actually means is "reference to a boxed copy of a System::Single". The compiler tries to make sense of that, inevitably it starts to get very confused what you try to do next. Like using the [] operator, that requires the type to have an indexer. A float doesn't have one.

You need either:

Example(array<float>^ myarray2)

Which is safe and verifiable, you can't index the array out of bounds. And you don't have to hard-code "3" anymore, you can simply use myarray2->Length instead. And you don't (usually) have the copy the array anymore, simply assign myarray1. You'd call the constructor by passing gcnew array<float> { 1, 2, 3 }.

Or:

Example(float* myarray2)

Which works just like the way it does in C and C++. And required if you want to call the constructor with that float[]. Not verifiable, you need that magic "3". Do consider adding an extra argument to pass the array length.

Upvotes: 2

Related Questions