tags:

views:

171

answers:

2

Hello,

I have spotted something like this in code:

void foo(IList<int>^ const & list ) { ... }

What does this ^ const& mean? I looked in the C++/CLI specification, but found no comments on making constant tracking references, nor the ^& combo.

Is this legal?

+1  A: 

It's a reference which is constant to a tracking handle.

It allows you to pass the handle by reference instead of by value. Presumably the author thinks it's more efficient than copying the handle.

If the author meant to make the handle constant he should have used either of

Method(TestClass const ^ const & parameter) 
Method(TestClass const^ parameter)

Or alternatively Method(TestClass const^& parameter) - but the caller must const up the handle first with TestClass const^ constHandle = nonConstHandle

An example of each:

// test.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

ref class TestClass
{

public:

    void setA(int value)
    {
        a = value;
    }



    TestClass() : 
        a(10)
    {
    }

private:    

    int a;
};

class TakesHandle
{
public:

    void methodX1(TestClass const ^ const & parameter)
    {
    // Un-commenting below causes compiler error
    // parameter->setA(11);
    }

    void methodX2(TestClass const^ parameter)
    {
    // Un-commenting below causes compiler error
    // parameter->setA(11);
    }

    void methodX3(TestClass const^& parameter)
    {
    // Un-commenting below causes compiler error
    // parameter->setA(11);
    }

};


int _tmain(int argc, _TCHAR* argv[])
{
    TakesHandle takes;

    TestClass ^ test1 = gcnew TestClass();

    // compiles
    takes.methodX1(test1);

    // compiles
    takes.methodX2(test1);

    TestClass const ^ constHandle = test1;
    takes.methodX3(constHandle);

    return 0;
}
LukeN
Vulcan Eager
So what's the difference between this construction and just passing the tracking pointer (`IList<>^`) by value?
liori
@liori no. 2 - If you pass the tracking pointer as in your comment it wouldn't get the constness.
LukeN
My mistake @Vulcan Eager. It's not the same.
LukeN
+1  A: 

This code was probably written by a C++ programmer that used common C++ idiom to write C++/CLI. It is quite wrong, passing a reference to tracking handle is only possible if the handle is stored on the stack. It cannot work if the passed List<> reference is stored in a field of an object on the heap, the garbage collector can move it and make the pointer invalid. The compiler will catch it and generate an error. The ^ is already a reference, no additional reference is needed.

Without the reference, the const keyword doesn't make a lot of sense anymore either. Not that it ever did before, the CLR cannot enforce it. Not that this mattered much here, this code could not be called from any other .NET language. They won't generate a pointer to the tracking handle.

Just fix it, there's little point in keeping bad code like this:

 void foo(IList<int>^ list ) { ... }

Example of code that shows that the reference cannot work:

using namespace System;
using namespace System::Collections::Generic;

ref class Test {
public:
    IList<int>^ lst;
    void foo(IList<int> const &list) {}
    void wontcompile() {
        foo(lst);  // C3699
        IList<int>^ okay;
        foo(okay);
    }
};
Hans Passant
I came to similar conclusion. The problem is, the function is in an external library which I was advised to use. I managed to reimplement it in my own code. Thank you for confirming my worries.
liori
You're partially correct. You most definitely can pass a reference to a handle to the managed heap.The problem is that you can't use const methods on a ref type, so c++/cli doesn't let you do proper const correctness.The runtime won't cast something to const for you automagically, but if you mark the parameter as const, you form a contract with the caller that you aren't going to change their object.
LukeN
No, where the reference is stored matters. "Const" is encoded in attributes in the assembly's metadata, other languages will ignore them.
Hans Passant
LukeN is absolutely correct. C++/CLI doesn't let you define `const` methods, so it's impossible to write const-correct C++/CLI code. Plus there's a total lack of const-correct attributes within the BCL.
Ben Voigt
I looked at this again and edited my answer above. The original code sample in the question is trying to pass the handle by reference instead of passing it by value. Marking the reference const prevents the handle being reassigned.
LukeN
@LukeN: I don't think we're talking about the same thing. I added a code snippet that shows the problem with references.
Hans Passant
@Ben, what do you mean by "const-correct attributes", and why `modopt(IsConst)` isn't it? That it doesn't get respected by pretty much any other language is a different story...
Pavel Minaev
@Hans: It will work, the caller needs to make a const handle to the object first, the compiler won't do it for you.In your example you are trying to convert a handle to a const native reference, which won't work as it doesn't make sense as doesn't match the original question.Assuming it's foo is edited to be void foo(IList<int> const^ You would then be able to call foo(constList) without errors. I do this in the example code in my answer and it compiles fine in 2008.
LukeN
@Luke, well, that would be a possible workaround. It doesn't have anything to with const, "okay" isn't const in my snippet. Location matters.
Hans Passant
@Pavel, I mean C++/CLI lets you make a `const Control^`, but it is not useful to do so because even readonly operations are not allowed through a const handle. e.g. `myArray->size()` works just fine on a `const std::vector*` but you can't read the `Length` property of an `ArrayList`. Going beyond const member functions, you cannot even pass two `const System::Object^` to `ReferenceEquals`, because the attributes needed for const-correct usage are *completely* missing from BCL classes.
Ben Voigt