C++ Logo

std-proposals

Advanced search

Re: [std-proposals] std::elide

From: Frederick Virchanza Gotham <cauldwell.thomas_at_[hidden]>
Date: Sun, 10 Mar 2024 14:28:38 +0000
On Sun, Mar 10, 2024 at 11:55 AM Jens Maurer wrote:
>
> Maybe [temp.deduct.general] p8 or so would be a more appropriate place
> to talk about SFINAE stuff.


Yeah, if I want the substitution to fail without a compiler error --
and without the code becoming ill-formed -- then I should look a
slipping a paragraph somewhere into that section.


> Why do you need an extra function instead of using class template
> argument deduction for std::elide_t directly?


I spent a few hours trying to combine the class and the function into
one. It's possible to do so if the callable is a simple function
pointer. However if the callable has multiple function-call-operators
then you need both a function and a class, i.e. 'elide' and 'elide_t'.
Consider the following class I used to torture-test it:

    class Tester {
    public:
        counting_semaphore<5> operator()(void) &
        {
            return counting_semaphore<5>(1);
        }
        counting_semaphore<8> operator()(void) const &
        {
            return counting_semaphore<8>(1);
        }
        counting_semaphore<7> operator()(void) const &&
        {
            return counting_semaphore<7>(1);
        }
        counting_semaphore<6> operator()(void) &&
        {
            return counting_semaphore<6>(1);
        }
    };


If you are able to write just one class to accommodate such a bizarre
test class, without needing to have a separate standalone function,
then I'll be very intrigued to see how you pulled it off. I spent a
few hours at it, even trying stuff like:

    operator decltype(auto)()
    {
        return std::apply(f, args);
    }

I wasn't able to retain the Lvalue-or-Rvalue-ness of the function
parameters without separating it into a class and a standalone
function.


> So, without the core language change, if AwkwardClass ever wants to
> participate in no-copy creation, it would need to add a requires-clause.


Yes, the template constructor would need the following constraint:

    requires (!is_specialization_v<remove_cvref_t<T>, elide_t>)


> Seems a small price to pay.


But the whole point of all of this is that we don't have to edit the
original classes such as std::optional, std::variant, boost::whatever.

If the class has a canonical 'emplace' method, then this proposal can
enable us to elide the copy/move operation without the need to edit
the original class definitions.
If we can edit the original class definitions then we may as well just
add a method called 'emplace_with_return_value_from_invocation_of'.


> I'd like to point out that we already have std::piecewise_construct for
> a similar purpose.


Interesting, I hadn't seen this before. A good example of where the
distinction between language and library has become mushed.


> For the avoidance of doubt, I'm opposed to the core language change.


Until you're dealing with allocators. The below code will invoke the
constructor of 'MonkeyAllocator' with T set to a specialisation of
'std::elide_t' -- which of course we don't want.

class Monkey {};

struct MonkeyAllocator : std::allocator<Monkey> {
    std::mutex m; // cannot move, cannot copy

    template<typename T>
    MonkeyAllocator(T &&arg) : std::allocator<Monkey>( std::forward<T>(arg) )
    {
        cout << "MonkeyAllocator constructed with T = " <<
typeid(T).name() << endl;
    }
};

MonkeyAllocator GiveMeMonkeyAllocator(void)
{
    return MonkeyAllocator( std::allocator<int>() );
}

int main(void)
{
    std::optional<MonkeyAllocator> var;
    var.emplace( std::elide(GiveMeMonkeyAllocator) );
}

Received on 2024-03-10 14:28:48