1

I have a class which overloads the function call operator with a template function, like so:

class Test
{
public:
    template<class T>
        void operator()(T t)
    {
        std::cout<<(&t)<<std::endl;
    };
};

I'd like to call it with a reference argument, however when trying to do so, it passes the argument as a value instead. Here's my test setup:

template<class T>
    void test(T t) {std::cout<<(&t)<<std::endl;}

int main(int argc,char *argv[])
{
    Test t;
    int i = 5;
    std::cout<<(&i)<<std::endl;
    t((int&)i); // Passes the argument as a value/copy?
    test<int&>(i); // Passes the argument as a reference
    while(true);
    return 0;
}

The output is:

0110F738 -- Output of the address of 'i'

0110F664 -- Output of the address of the argument in the template overload

0110F738 -- Output of the address of the argument through 'test'

The template function 'test' is merely for validation.

The visual studio debugger confirms that it's using 'int' instead of 'int&' for the template overload:

test_function_call.exe!Test::operator()(int t) Line 9 C++

How can I force it to use a reference instead? Is there a way to specify the types using <> on a template function call operator?

Community
  • 1
  • 1
Silverlan
  • 2,783
  • 3
  • 31
  • 66

2 Answers2

2

That's because in your case the cv-qualifiers and the reference-ness of the parameter are discarded when performing template type deduction. Pass via a std::ref wrapper instead

t(std::ref(i));

Simple example:

#include <iostream>
#include <functional>

template<typename T>
void f(T param)
{
    ++param;
}

int main()
{
    int i = 0;
    f(std::ref(i));
    std::cout << i << std::endl; // i is modified here, displays 1
}
vsoftco
  • 55,410
  • 12
  • 139
  • 252
  • Didn't know about std::ref, however the behavior doesn't change, it's still passing it as a value. – Silverlan May 22 '15 at 17:58
  • @Silverlan How do you know? It will "simulate" passing by reference if you e.g. modify `t` inside the `operator()`. Do something like `++t` inside, and you'll see the `i` will be modified. – vsoftco May 22 '15 at 17:58
  • 1
    That simulation is not the real thing- there are numerous ways to distinguish between `T&` and `std::reference_wrapper`. – Puppy May 22 '15 at 18:01
  • You're right, it actually does work. But how come it still gives me a different address inside the operator() for 't'? – Silverlan May 22 '15 at 18:02
  • @Puppy indeed, I couldn't come up with a better wording ;) – vsoftco May 22 '15 at 18:02
  • @Silverlan because `std::ref` is an object of a different type that wraps internally a reference to what you passed, it just "looks" like a reference. – vsoftco May 22 '15 at 18:02
  • Thanks, that makes sense. It's working as it should now. – Silverlan May 22 '15 at 18:03
1

You may use universal reference:

class Test
{
public:
    template<class T>
    void operator()(T&& t)
    {
        std::cout<<(&t)<<std::endl;
    };
};
Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • Would I still be able to pass it as a value/copy using this way? I'd like to be able to do either. – Silverlan May 22 '15 at 18:11
  • 2
    @Silverlan not really, the type for `t` will be either deduced as `T&` or `T`, which after [reference collapsing](http://thbecker.net/articles/rvalue_references/section_08.html) become `T&` and `T&&`. – vsoftco May 22 '15 at 18:17