3

I use anonymous variables all the time, when I have no need of a named object hanging around for the entire present scope.

I need to work with a function - outside my control - that takes a pointer as an argument and does not NULL-check. I was hoping to be able to pass the address of an anonymous variable (because I don't care about the value written at the dereferenced address), but get hit with a compile error. The simplified example below...

#include <iostream>

void ptrFunc( const int* p )
{
  if ( p )
  {
    std::cout << "*p == " << *p << std::endl;
  }
}

void refFunc( const int& i )
{
  std::cout << "(ref)i == " << i << std::endl;
}

void valueFunc( int i )
{
  std::cout << "i == " << i << std::endl;
}

int main( int argc, char* argv[] )
{
  valueFunc( int() );   // This is fine.
  refFunc( int() );     // This is also fine.
  ptrFunc( &(int()) );  // This doesn't compile.
}

...generates this compile error:

>g++ -g main.cpp 
main.cpp: In function 'int main(int, char**)':
main.cpp:25:19: error: lvalue required as unary '&' operand
   ptrFunc( &(int()) );  // This doesn't compile.
                   ^
>g++ --version
g++ (GCC) 4.8.3 20140911 (Red Hat 4.8.3-7)
Copyright (C) 2013 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.

The compiler error is pretty readable: an lvalue is required to use the address operator. But I'm wondering if the community can help me understand the rationale behind this restriction. It seems to me that my attempted use could reasonably be legal because the anonymous variable's lifetime is up to the semicolon, i.e. it is "alive" during ptrFunc()'s lifetime.

I considered whether it was considered "dangerous" to allow pointers to anonymous variables because of their shortened lifespan. But really any use if pointers - even for lvalues or heap-allocated objects is subject to the same problem: if someone is hanging onto any pointer, there's always the danger it could point to invalidated memory. Use of pointers is inherently dependent on careful coding, and I don't see this attempted use-case as especially different in that regard.

I'm curious to understand the rationale behind this. Thank you.

What else have I tried?

Tried compiling the example code with gcc-4.9.2 online to the same effect; so this issue is not a limitation/bug from an outdated compiler.

StoneThrow
  • 5,314
  • 4
  • 44
  • 86
  • FWIW, it's not too difficult to work around that limitation. You can use a lambda function as a wrapper around the call to `ptrFunc`. Such as: `[](int i){ptrFunc(&i);}(int());` – R Sahu Mar 24 '17 at 02:05
  • @RSahu Clever, but I'm stuck with C++98 in this development environment. – StoneThrow Mar 24 '17 at 02:08

3 Answers3

1

Ultimately what you're asking for here doesn't exist for good reason: A value with an address has to be located somewhere, and therefore have some scope/lifetime. The poiner you would be passing to a function is an address. It can be read from multiple times, and, when non-const, written to.

The compiler doesn't know how a calling function will use your parameters, and wants the freedom to put rvalues around, including folding them into constants in the code, or putting them in registers. You can't take an address to that and pass it to a function.

What you're asking for seems to reduce to compiler sugar that creates a local on the stack, takes a reference to it, and passes it to a function, but hides its address from the code so that no other code can use that value. C++ is built on explicit memory management, and thus is opposed to that.

Ok, but I really want to do this

Just for fun, though, I think you can do something like:

#include <memory>
#include <iostream>

void ptrFunc( const int* p )
{
  if ( p )
  {
    std::cout << "*p == " << *p << std::endl;
  }
}

int main()
{
  ptrFunc(std::unique_ptr<int>(new int()).get());
  return 0;
}

How does this work?

Awfully, in that it is very inefficient. Syntactically, it does what you want it to, though:

ptrFunc(                 // 1. call ptrFunc
  std::unique_ptr<int>(  // 2. instantiate a unique_ptr as a temporary
                         //    this takes a pointer to a heap-allocated value
                         //    and assumes ownership of it. it deletes that
                         //    value when it goes out of scope. for a
                         //    temporary, this happens after the expression.
    new int()            // 3. heap-allocate an integer
  ).get()                // 4. returns a raw-pointer to the heap-allocated
                         //    value owned by the unique_ptr. That value
                         //    becomes invalid memory if accessed after the
                         //    unique_ptr destroys it.
);
EyasSH
  • 3,679
  • 22
  • 36
  • "The compiler...wants the freedom to put rvalues around, including folding them into constants in the code, or putting them in registers. You can't take an address to that and pass it to a function." Let me see if I understand you correctly: rvalues (like my anonymous variable) could be placed by the compiler in a register (because it is const), and the compiler disallows getting the address of a register. Is that right? – StoneThrow Mar 24 '17 at 02:02
  • MSVC has a strange language extension that enables this kind of thing. e.g. line 475 at https://git.io/fpKY1 (Frank Luna's code, not mine). This is discussed at https://stackoverflow.com/a/16570731/ and https://stackoverflow.com/a/2280780/ – Max Barraclough Dec 02 '18 at 11:32
1

But I'm wondering if the community can help me understand the rationale behind this restriction. It seems to me that my attempted use could reasonably be legal because the anonymous variable's lifetime is up to the semicolon, i.e. it is "alive" during ptrFunc()'s lifetime.

In this case, yes, it would work. In general, no.

References cause the temporary's lifetime to be extended. Pointers don't. That is, given:

int main() {
    const int & a = 1;
    int && b = 2;
    const int * c = &static_cast<const int &>(3); // don't do this
    return a + b - *c;
}

the references a and b continue to refer to valid objects. The pointer c does not, and there is no reasonable way the lifetime extension rules could work for pointers without a significant fundamental change in what pointers are. It's doable for references because references are a new (compared to C) language feature, and as a new language feature, there was no problem making them immutable. Pointers have a lot of existing code using them that would be invalidated by a change in the rules.

  • You addressed a question I had overnight with @EyasSH's answer, but it's worth bringing up wrt your answer too: references are really pointers under the hood, with syntactic sugar applied over them to remove the need to dereference references. Given that, why are there different lifetime extension rules for pointers and references? Is that "just how the language is"? – StoneThrow Mar 24 '17 at 16:38
  • It may also be worth asking my earlier question RE: EyasSH's answer (I don't think he's active on this question anymore): since pointers and references are the same "under the hood", if the compiler wanted to place a const in a register (EyasSH says it's illegal to get the address of a register: true?), then why is there a difference in legality whether that register address is referenced by reference or pointer? – StoneThrow Mar 24 '17 at 16:41
  • 1
    "references are really pointers under the hood" -- Not always. This is one of the differences. There are some more, for example compilers being allowed to optimise away reference members of classes in some cases where that same permission cannot be granted for pointers, due to backwards compatibility. Anyway, think about what a rule specifying lifetime extension for pointers would have to look like. It isn't possible, and if you think you have a rule that would work, I'll be happy to provide either a situation where it doesn't work, or an inconsistency in the resulting language. –  Mar 24 '17 at 16:45
  • "(EyasSH says it's illegal to get the address of a register: true?)" -- Why depends on what you mean by "register", but I think that is not true or misleading either way. –  Mar 24 '17 at 16:47
  • The nitty-gritty of C++ semantics continues to fascinate me. Thanks for your answers. – StoneThrow Mar 24 '17 at 18:22
0

C++11 introduces std::addressof() to get the address of a variable, even if it is a class type that overrides operator&. You could apply a similar technique to get the address of a temporary variable, eg:

template <class T>
const T* addrtemp(const T& arg)     
{    
    return reinterpret_cast<T*>(    
        &const_cast<char&>(    
            reinterpret_cast<const volatile char&>(arg)
        )
    );    
}

ptrFunc( addrtemp(int()) );

Demo

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770