Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ template allows discard of const reference qualifier

Why does this code compile? (tested with g++ and clang++)

The following code is for a factory method that accepts a function and creates a forwarding std::function out of it. As you can see, the lambda inside accepts const Arg& arguments and forwards them to the given function.

In main() I use factory() to create a forwarder to test_func(), which accepts a non-const reference parameter. What I don't understand is why this doesn't produce an error about discarding the const qualifier from the argument.

Note that indeed the instance of the class C, created in main(), is passed without creating any copies.

#include <functional>
#include <iostream>

class C
{
public:
        C() {}

        C(const C&)
        {
                std::cout << "C copy\n";
        }
};

int test_func(C& cref)
{
        return 0;
}

template<typename Ret, typename... Arg>
std::function<Ret (const Arg&...)> factory(Ret (*func) (Arg...))
{
        return [ func ] (const Arg&... arg) -> Ret {
                return (func)(arg...);
        };
}

int main() {
        auto caller = factory(test_func);
        C c;
        caller(c);
        return 0;
}
like image 889
gnobal Avatar asked Oct 29 '13 09:10

gnobal


1 Answers

As mentioned in the comments, you should use perfect forwarding (see Scott Meyers's presentation on Universal reference).

In your case, it should be :

#include <functional>
#include <iostream>
#include <utility>

class C
{
public:
        C() {}

        C(const C&)
        {
                std::cout << "C copy\n";
        }
};

int test_func(const C& )
{
        return 0;
}

template<typename Ret, typename... Arg>
std::function<Ret (Arg...)> factory(Ret (*func) (Arg...))
{
        return [ func ] (Arg&&... arg) -> Ret {
                return func(std::forward<Arg>(arg)...);
        };
}

int main() {
        auto caller = factory(test_func);
        const C c;
        caller(c);
}

Take a note that I changed C c; to const C c; in your main(), and I modified the test_func.


If you want to avoid creating copies, you have to make sure the test_func function it not taking by value. It should take by reference (const of non-const).

like image 73
BЈовић Avatar answered Sep 19 '22 19:09

BЈовић