Strange C2664 in VC 7.1, VC8

Hi,

While porting some C++ code from VC7 to VC8 (2005.NET) I encountered a strange version of compiler error C2664. The actual code was of course much more complicated, but I have managed to reduce the problem to this 40-liner:

// CODE BEGIN
class Class1
{
public:
  virtual Class1 * Copy() const = 0;
};

class Class1Deriv : public Class1
{
public:
  Class1 * Copy() const
  {
    return (Class1 *)(new Class1Deriv(*this));
  }
};

class Class2
{
public:
  Class2(const Class1 &arg1);
};

class Class3
{
public:
  Class3(const Class2 &arg2);
};

void main(void)
{
  const Class2 *pArg2 = new const Class2(Class1Deriv());
    // This works using VC8, VC7.1 (2005.NET, 2003.NET), but the above
    // line fails with C2468 using VC7 (2002.NET):
  Class3 InstVC8(*pArg2);
  delete pArg2;

  const Class2 Arg2(Class1Deriv());
    // This works using VC7 (2002.NET), but fails with C2664 using VC7.1
    // or VC8 (2003.NET, 2005.NET):
  Class3 InstVC7(Arg2);
}
// CODE END

The strange thing is, that new-instantiation of Class2 gets around the problem in VC8, while new-instantiation fails in VC7. I would think, that *pArg2 and Arg2 should be completely compatible. The actual error messages for VC7.1, VC8 are:

c2664.cpp(39) : error C2664: 'Class3::Class3(const Class2 &)' : cannot convert parameter 1 from 'const Class2 (__cdecl *)(Class1Deriv (__cdecl *)(void))' to 'const Class2 &'
        Reason: cannot convert from 'overloaded-function' to 'const Class2'
        No constructor could take the source type, or constructor overload resolution was ambiguous

And for VC7:

c2664.cpp(30) : error C2468: 'new' : cannot allocate 'const'/'volatile' objects (type is 'const Class2')

Does anybody have an explanation for this behavior, or is it a compiler bug Is there any way to work around the problem WITHOUT having to use new-instantiation of Class2

Thanks in advance,
Paul



Answer this question

Strange C2664 in VC 7.1, VC8

  • Marty1066

    I see a couple of things:

    const Class2 *pArg2 = new const Class2(Class1Deriv());

    In theis case the compiler is correct: the C++ Language does not allow you to allocate a const (or volatile) object. The fix for this problem is to remove the 'const'.

    The second problem:

    const Class2 Arg2(Class1Deriv());

    According to the C++ Language if something can be interpreted as a declaration then it is a declaration: and this code can interpreted as a local function declaration.

    It declares a function called Arg2 the return a const Class2 and takes as its single parameter an instance of a Class1Deriv. The '()' are interpreted by the compiler as an unnecessary, but allowed, set of parameters around the missing name of the function parameter.

    The fix here is to change the code:

    const Class2 Arg2 = Class1Deriv();


  • John Portnov

     wrote
    
    >I see a couple of things:
    >
    > const Class2 *pArg2 = new const Class2(Class1Deriv());
    >
    > In theis case the compiler is correct: the C++ Language does not allow
    > you to allocate a const (or volatile) object. The fix for this problem is 
    > to
    > remove the 'const'.
    
    What's wrong with allocating a const (or volatile) object  As long
    as the object has an initializer (which it has in the above case) I don't
    see any problems 
    
    > The second problem:
    >
    > const Class2 Arg2(Class1Deriv());
    >
    > According to the C++ Language if something can be interpreted as a
    > declaration then it is a declaration: and > this code can interpreted as a
    > local function declaration.
    >
    > It declares a function called Arg2 the return a const Class2 and takes as
    > its single parameter an instance of a Class1Deriv. The '()' are 
    > interpreted
    > by the compiler as an unnecessary, but allowed, set of parameters
    > around the missing name of the function parameter.
    
    I don't see that either. Why should the parens be unnecessary  To me,
    this rather looks like a function delcaration taking as a parameter
    another function taking no parameters and returning a Class1Derived.
    
    i.e. the same as (since function parameters decay)
    const Class2 Arg2( Class1Derived (*)() );
    
    or perhaps more readable
    
    typedef Class1Derived some_func_t();
    const Class2 Arg2( some_func_t );
    
    while
    const Class2 Arg2( Class1Derived );
    
    is a function taking a Class1Derived object.
    
    Am I missing something 
    
    Another possibility to avoid the problems would be another set
    of the '()' around the declaration
    
    const Class2 Arg2 ((Class1Derived());
    
    this should work for explicit constructors as well.
    
    -hg 
    
    
    
    
    
    
    
                                                
  • Need_Helpss

    For some reason, VC71 sees your definition of Arg2 as a function definition.

    I have no problem with

    Class1Deriv c1;

    const Class2 Arg2( c1 );
    --
    David


  • Bloke

    Well, my point was that I believe the new const X is indeed allowed in C++. I don't see anything in the standard that forbids that.

    You might argue the same for non-POD const objects with static or automatic storage duration. After all, it's perfectly valid to write:



    void foo() {
       const std::string s("snoopy");
    }

     


    The operator new/malloc is simply substituted by a compiler calculated stack frame relative address. I.e.:

    new (&s) X;

    And according to 12.1/4:
    ... a constructor shall not be declared const, volatile or const volatile (9.3.2). const and volatile semantics (7.1.5.1) are not applied on an object under construction. Such semantics only come into effect once the constructor for the most derived object (1.8) ends.

    Am I missing something

    -hg [aka NNTPUser]

  • double americano

    In C++ the creation of a dynamic lifetime object has two steps:

    1) The require memory for the object is allocated
    2) The constructor is run on the allocated memory initializing the object

    So the expression:

    new X(4);

    can really be thought of as:

    X* tmp = malloc(sizeof(X));  // Allocate the memory
    new (tmp) X(4);              // Initialize the object


    If C++ allowed:

    new const X(4);

    then the meaning would be:

    const X* tmp = malloc(sizeof(X));
    new (tmp) X(4);


    Which would fail to compile because you can't have a 'const' constructor and you would not be able to call a regular constructor on a const object (due to the loss of const in the conversion of the this-pointer).

    My comment about 'unnecessary parenthesis' referred to the fact that C (and hence C++) allows a set of parenthesis around the name in a declaration. For example:

    int (a);

    and even:

    int ((((a))));
    void f(int (a));


    But you are correct the interpretation of:

    const Class2 Arg2(Class1Derived());

    should be:

    const Class2 Arg2(Class1Derived(*)());

    Apologies: this is what happens when you try to answer questions when you don't have access to a compiler (or a copy of the C++ Standard) and hence can't confirm that the language acts the what you think it shouldSmile



  • Strange C2664 in VC 7.1, VC8