Enforce exact invokable signature in C++

i have a function that takes an invokable, and i want to make sure that the signature of the passed invokable is exactly the specified one.

#include <type_traits>
#include <cassert>
#include <iostream>

template<typename F>
requires std::is_invocable_v<F, float&>
float fn(F&& f) {
    float v;
    f(v);
    return v;
}

int main() {
    float v = 17.0;
    std::cout << "v=" << v << std::endl;   // returns v=17

    float v2 = fn([&v](float& b){
        b = v; 
    });
    std::cout << "v2=" << v2 << std::endl; // returns v2=17

    float v3 = fn([&v](float b){
        b = v;
    });
    std::cout << "v3=" << v3 << std::endl; // returns some random value (uninitialized)

    return 0;
}

https://godbolt.org/z/T7xGsqzvn

Here v3 is left uninitialized because the lambda takes the argument by value instead of a reference.

My question is now, how can i make sure that an invokable passed to fn always takes by reference, co my code works.

Thank you.


Solution 1:

You may add another constraint to fn:

template<typename F>
requires std::is_invocable_v<F, float&>
         and (not std::is_invocable_v<F, float>)  // <--
float fn(F&& f) {
    //...
}

Live here.