-1

Here

https://youtu.be/dTeKf5Oek2c?t=2939

Stephen T. Lavavej says in a talk that const auto && is not useful.

Isn't the following a valid use case?

NonMovableNonCopyable create () {
   // ...
   return {};
}

const auto && obj = create ();

Concrete application: In XLL code, MS Excel generally does not like its xlopers to be copied or moved which it returns, since after copying or moving it will not be able to correctly free them.

JohnB
  • 13,315
  • 4
  • 38
  • 65
  • 3
    You can accomplish the exact same thing with `const auto &`. – StoryTeller - Unslander Monica Jul 05 '16 at 07:25
  • 2
    FYI, you can right-click YouTube videos at the desired time and choose "Copy video URL at current time" which gives you a link direct to that time: https://youtu.be/dTeKf5Oek2c?t=2937 – Tas Jul 05 '16 at 07:26
  • 4
    You should probably explain why `const auto&&` is deemed not useful in the body of the question. – juanchopanza Jul 05 '16 at 07:32
  • @StoryTeller: You're right. I completely overlooked that point, since if I omit the const, `auto &&` works, yet `auto &` does not (as expected, of course). – JohnB Jul 05 '16 at 07:45
  • Might be worded better, but it's an important question IMHO. Something that everyone runs into sooner-or-later, and too many people accept the 'it makes no sense' answer. – lorro Jul 05 '16 at 14:32

2 Answers2

1

Const T && is very useful if T has a mutable field. A common example is a bool m_movedFrom that's initialized to false and gets set to true when moved from. This allows the rest of your object - resource handle, for example - to remain const.

class Resource // use this as a base class of const correct classes
{
private:
    mutable bool m_movedFrom;

protected:
    Resource()
    : m_movedFrom(false)
    {
    }

    Resource(const Resource&& other)
    : m_movedFrom(false)
    {
        other.m_movedFrom = true;
    }

    bool isOwning() const // call from descendant's destructor
    {
        return m_movedFrom;
    }
};

EDIT: a more complicated example explaining when the object is itself const, but a global state is not (not claiming this is the good way to solve this, it's for illustrational purposes only):

#include <iostream>
#include <string>
#include <vector>
#include <algorithm>

typedef std::string object_type;
typedef std::string seek_type;

class GoalSeeker
{
private:
    static std::vector<const GoalSeeker*> s_store;

    const std::vector<const GoalSeeker*>::iterator m_iter;
    const object_type                              m_data;

public:
    GoalSeeker( const object_type& data )
    : m_iter( s_store.insert( s_store.end(), this ) ), m_data(data)
    {
    }

    GoalSeeker( const GoalSeeker& ) = delete;

    GoalSeeker( const GoalSeeker&& other )
    : m_iter( other.m_iter ), m_data( other.m_data )
    {
        *m_iter = this;
    }

    ~GoalSeeker()
    {
        if( *m_iter == this )
        {
            // cleanup m_data
        }
    }

    static bool seek( const seek_type& needle )
    {
        return std::find_if(s_store.begin(), s_store.end(),
            [&needle](const GoalSeeker* haystack)
            {
                return haystack->m_data.find(needle) != std::string::npos;
            }) != s_store.end();
    }
};

std::vector<const GoalSeeker*> GoalSeeker::s_store = {};

GoalSeeker fn()
{
    return GoalSeeker("def");
}

int main() {
    GoalSeeker a( "abc"                     );
    GoalSeeker b( "cde"                     );
    GoalSeeker s( (const GoalSeeker&&) fn() );

    std::cout << GoalSeeker::seek( "de" ) << " " << GoalSeeker::seek( "fff" );
}
lorro
  • 10,687
  • 23
  • 36
  • 1
    Not sure I understand you meaning here. I doesn't quite make sense to me. Can you provide a concrete example, please? – Cheers and hth. - Alf Jul 05 '16 at 07:42
  • Edited to provide an example. – lorro Jul 05 '16 at 10:55
  • Okay, in the code you work with there are C++11 functions returning `const T`. That's *not a good idea*. At a guess it's from reading old pre-C++11 stuff by Scott Meyers, who once advocated that for C++03. It's sufficiently uncommon that it did not pop up in my mind when considering possible scenarios. So, the `const&&` didn't entirely make sense. – Cheers and hth. - Alf Jul 05 '16 at 11:08
  • @Cheersandhth.-Alf : Was that comment for me? Where exactly do I return `const T`? And why did you conclude `const&&` makes no sense, given the above example? Another use case: you have a key class that can index (by some field's value) into a global map that stores (amongst other things) a non-owning pointer to the currently owning key. Move constructor obviously changes this key *in the map*, but not the object you moved from. Hence it's `const&&`. – lorro Jul 05 '16 at 14:31
  • Yes, it was for you. And this code doesn't itself return a `const T`, but the constructor taking `const T&&` only makes sense if there is some function `f` that does, where `f()` is used as argument to the constructor. Otherwise just make that an ordinary moving constuctor, with `T&&` argument. – Cheers and hth. - Alf Jul 05 '16 at 15:31
  • Note that *a temporary object isn't `const`*, unless it's created witht type `const T` (which is rather uncommon). – Cheers and hth. - Alf Jul 05 '16 at 15:33
  • @Cheersandhth.-Alf: If I get you right, you're proposing to hold all resources non-const, even in members (you cannot move from a const field). I prefer to keep everything const until I need to change it (and can't copy), and then have the minimum of it as non-const. In case of a resource, once I've acquired it (preferably in named constructor) I wouldn't want to allow changing it even it the class itself. However, I need a minimalistic signal on whether to close the file, delete the array, free the resource. That's what the first example was for; given a second one. – lorro Jul 05 '16 at 18:02
1

Instead of

const auto && obj = create ();

... write just

const auto object = create();

... or

const auto object{ create() };

The = relies on the compiler eliding the copy constructor call, but I don't know about any extant compiler that fails to do that.

The clarity gained is IMO much more important than the guarantee offered by the reference (and if using reference, do use an ordinary & reference). Namely, one avoids having a maintenance programmer wasting time on trying to understand the rationale for the reference. If the type was explicitly specified it could be a case of polymorphic reference, Petru Marginean's trick, but with auto that's out of the question, and so the maintenance programmers are left scratching their heads, for some period of paid time.


On the other hand, const T&& can be useful for function overloading as an argument type to catch the case of a temporary as argument, in the same way as && qualifier for member function was considered sufficiently useful to be adopted in the standard. For example, even though I do not recommend this, if a pointer to the actual argument is stored for later use, then presumably one doesn't want to store a pointer to a temporary, which will end up as a dangling pointer:

struct Expr{ virtual ~Expr(){} };

struct Sum_expr: Expr
{
    const Expr*     a_;
    const Expr*     b_;

    Sum_expr( Expr const& a,Expr const& b ): a_( &a ), b_( &b ) {}

    template< class A >
    Sum_expr( A&&, Expr const&& b ) = delete;

    template< class B >
    Sum_expr( Expr const&& a, B&& ) = delete;
};

auto main()
    -> int
{
    Expr a;
    Expr b;
    Sum_expr sum{ a, b };
    Sum_expr sum2{ a, Expr() };     //! Nope, won't compile.
}

Note: Here A&& and B&& support both rvalue and lvalue actual arguments, i.e. they're not necessarily rvalue references, because they're universal references.

But instead of overloading and differentiating the cases I think I'd make that formal argument a pointer, even if a pointer technically can be a nullpointer, because as I see it, with what I'm accustomed to, it communicates the intent more clearly.

Sum_expr( Expr const* a, Expr const* b );
Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331