Giter VIP home page Giter VIP logo

Comments (5)

zhihaoy avatar zhihaoy commented on May 24, 2024 1

It is temporarily by design. You may note that std::move_only_function supports no CTAD (other than moving itself). The reasons are

  1. move_only_function is meant to erase type from multiple objects. Therefore, it should declare a type; otherwise, we're reducing all callable objects to a type similar to the first one that initializes the wrapper.
  2. The top 3 places where type-erased callable wrappers appear are members, the value of key-value pairs in a container, and parameters. You cannot use CTAD in any of those.
  3. Unlike std::function, move_only_function can be ref-qualified, const-qualified, noexcept-qualified. It is unclear how CTAD would handle these. The design in my mind is that noexcept should be reflected in the deduced call signature; others should not. But as noted by Arthur O'Dwyer and myself, the trouble is that if two callables differ only in noexcept deduce to different erased-type, assigning one to the other will create a wrapper on top of the wrapper. It is worse in function_ref since there can be a dangling wrapper on top of a wrapper.

These issues apply to std::function_ref as well, plus that function_ref has the potential of introducing dangling reference in an initializing declaration -- unless the right-hand side is a function (pointer).

In contrast, the CTAD on std::function was somewhat experimental. It's not harmful because std::function is immune from 3), though.

My feeling is that, CTAD on function_ref (and move_only_function) need motivation. I want to see some context and motivating examples; the consistency argument (i.e., to be consistent with std::function) doesn't apply here.

from nontype_functional.

alabuzhev avatar alabuzhev commented on May 24, 2024

Thanks for the explanation.

Some context & motivation: I noticed that quite often I try to write

auto handler1 = [&](){};
auto handler2 = [&](){};

auto handler = condition?
    handler1 :
    handler2;

// Call handler more than once

This obviously does not work, because handler1 and handler2 are unrelated and have no common type.

Type-erasing them with std::function helps:

auto handler = condition?
	std::function(handler1) :
	std::function(handler2);

but obviously has an extra cost.

function_ref feels like a drop-in replacement (and improvement) in this context, but without CTAD it is more verbose (especially with complex signatures):

auto handler = condition?
	std23::function_ref<void()>(handler1) :
	std23::function_ref<void()>(handler2);

from nontype_functional.

zhihaoy avatar zhihaoy commented on May 24, 2024

If it is okay to spell the type once, maybe

auto handler = [&] -> std23::function_ref<void()> {
    if (cond)
        return handler1;
    else
        return handler2;
}();

https://godbolt.org/z/3r5Pqsdnr

If you definitely want some form of deduction, a weird trick is

auto handler =
    cond ? std23::function_ref(
               std23::nontype<&decltype(handler1)::operator()>, handler1)
         : std23::function_ref(
               std23::nontype<&decltype(handler2)::operator()>, handler2);

https://godbolt.org/z/bxhv45q4h

The trick can be an approach if you ditch operator() altogether. You can write

struct Handler1 {
    void fn() {}
} handler1;
struct Handler2 {
    void fn() {}
} handler2;

auto handler =
    cond ? std23::function_ref(std23::nontype<&Handler1::fn>, handler1)
         : std23::function_ref(std23::nontype<&Handler2::fn>, handler2);

https://godbolt.org/z/EsenY6jaK

from nontype_functional.

alabuzhev avatar alabuzhev commented on May 24, 2024

There are ways of course, but my goal was to reduce the typing and repeating myself, not to get some form of deduction just for the sake of it :)

The design in my mind is that noexcept should be reflected in the deduced call signature; others should not

Should it though? noexcept selling points are optimization opportunities and the contract, important in certain contexts, e.g. move ctors & operators. In case of function_ref optimizations are probably limited by the indirection anyway and I cannot immediately think of a good example where the noexcept-ness of it would affect the logic (except the infamous noexcept(noexcept(%the_whole_function_body%))). However, I am not an expert in this area.

the trouble is that if two callables differ only in noexcept deduce to different erased-type, assigning one to the other will create a wrapper on top of the wrapper

CTAD, just like auto, could deduce something completely different from the user's expectations in certain corner cases and cause troubles. However, I do not think it should be ditched completely because of that: as everything else, it is a tool that could make our lives easier in simple scenarios, should be used responsibly in complex ones and could always be ignored in favor of a more explicit way in case of any uncertainties.

from nontype_functional.

zhihaoy avatar zhihaoy commented on May 24, 2024

I can argue for noexcept, others can argue for const. Since our expectations are diverse, maybe the best approach is to have some traits that are used to fill signature types for all callable types. Like

static_assert(std::is_same_v<common_signature_t<std::function_ref, decltype(handler1), decltype(handler2)>,
                             std::function_ref<void()>);

and there can be common_signature_const_t, common_signature_nothrow_t, etc.

from nontype_functional.

Related Issues (3)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.