2

from the old C++98 i am aware, that return types a copied (by value) if not mentioned in the function declaration/definition otherwise with the address operator '&'.

Now i am playing around with the concepts of auto and decltype to let the compiler determin the return type. In an example i worte a class A where with exception of the default ctor any other ctors are deleted (class A is taken from a real project - and i investigate some issues). An object of the class A is contructed together with an etl::array (Embedded template library, Array created on the stack with fixed size), see example code below.

#include <etl/array.h>
#include <iostream>

class A {
public:
    A(std::uint8_t val) : _val(val){}
    A(A const&) = delete;      // copy delete
    A& operator=(A&) = delete; // copy assign delete
    A(A&&) = default;          // move default
    A& operator=(A&&) = delete;// move assign delete
    ~A() = default;
    void whoAmI(){std::cout << " I am A! " << std::endl;}
private:
    std::uint8_t _val;
};

decltype(auto) X(std::uint8_t val) {
  return etl::array<A,1>{A{val}};
}

int main()
{
    decltype(auto) x = X(5U);
    for (auto& it : x) {
      it.whoAmI();
    }
}

I would expect, that the etl::array will be copied in the main() routine and assigned to the local variable x. I would not expect to have a copy of A in the array, due to the deleted copy ctor. However, the code compiles and i am able to call the function on the element of the etl::array. I cannot understand why this is working and why it is compiling at all? And I wonder what type decltype(auto) finally is. I have chosen decltype(auto) because of Scott-Meyers Item 2 and Item 3. To item 3, i am not sure to have a complete understanding on the decltype topic..

When I step through the code it works fine, leaving me pussled behind..

Any help on this topic is highly appreciated!


Thank you very much for your help, it is enlightening to me. Now i finally know why it's working :-D - you made my day!

  • 2
    it'll be using the move constructor, did you mean to delete that too? – Alan Birtles Nov 17 '20 at 12:25
  • Returning a prvalue without any conversions is mandatory copy elision, which is allowed even in the absence of copy/move semantics. See the linked question for more information. – Sam Varshavchik Nov 17 '20 at 12:47
  • @SamVarshavchik There might be a good dupe for this, but the target you've added is not appropriate. The copy-elision doesn't apply directly to the OP's question since they have a move-constructor, and are compiling with C++14. The code doesn't actually compile without defaulting the move-constructor, and that's not really addressed in the target. – cigien Nov 17 '20 at 12:50
  • As mentioned in my last comment, I don't think the target is appropriate, so I'm voting to reopen the question. – cigien Nov 17 '20 at 12:59
  • Except that copy elision is exactly what's happening here. – Sam Varshavchik Nov 17 '20 at 13:04
  • @SamVarshavchik No, it's using the move-constructor. `delete`ing it makes the code [fail](https://godbolt.org/z/5556Td). Non-mandatory copy-elision is not allowed when the necessary constructors are not available (it just means they may not get *used*). – cigien Nov 17 '20 at 13:09
  • 1
    Fair enough, reopened. – Sam Varshavchik Nov 17 '20 at 13:13

1 Answers1

5

decltype(auto) is used to determine the type and the value category of an expression. When used as a return type, it lets the function decide what the returned value type should be, based on the expression in the return statement. In this example, since you are using a temporary in the returned expression, it will deduce to an rvalue.

In this declaration:

decltype(auto) x = X(5U);

the syntax is copy-initialization, which has the effect of initializing the variable x from the expression X(5U). You have a defaulted move-constructor, and the compiler uses this constructor to initialize x from the rvalue returned from X.

Note that from C++17, due to mandatory copy-elision, you could even delete the move constructor, and the code is still valid, since there is no constructor needed to initialize x.

cigien
  • 57,834
  • 11
  • 73
  • 112