2

I've looked over a few similar questions, but I'm still confused. I'm trying to figure out how to explicitly (not by compiler optimization etc) and C++03-compatible avoid copying of an object when passing it to a specialized template function. Here is my test code:

#include <iostream>

using namespace std;

struct C
{
  C() { cout << "C()" << endl;  }
  C(const C&) { cout << "C(C)" << endl; }
  ~C() { cout << "~C()" << endl;  }
};

template<class T> void f(T) { cout << "f<T>" << endl; }

// This shows two possible ways, I don't need two overloads
// If I do it like (2) the function is not called, only if I do it like (1)

template<> void f(C c) { cout << "f<C>" << endl; } // (1)

template<> void f(const C& c) { cout << "f<C&>" << endl; } // (2)

int main()
{
    C c;
    f(c);
    return 0;
}

(1) accepts the object of type C, and makes a copy. Here is the output:

C()
C(C)
f<C>
~C()
~C()

So I've tried to specialize with a const C& parameter (2) to avoid this, but this simply doesn't work (apparently the reason is explained in this question).

Well, I could "pass by pointer", but that's kind of ugly. So is there some trick that would allow to do that somehow nicely?

EDIT: Oh, probably I wasn't clear. I already have a templated function

template<class T> void f(T) {...}

But now I want to specialize this function to accept a const& to another object:

template<> void f(const SpecificObject&) {...}

But it only gets called if I define it as

template<> void f(SpecificObject) {...}

Basically what I want to do with this specialization is to adapt the SpecificObject to the template interface like

template<> void f(SpecificObject obj){ f(obj.Adapted()); } // call the templated version

EDIT2: Ok, I can force the const C& specialization to be called this way:

f<const C&>(c);

but is there a way to make it work like this as just f(c)?

EDIT3: If someone would eventually have similar questions, I finally found this link in another question, and it is helpful: http://www.gotw.ca/publications/mill17.htm

Community
  • 1
  • 1
Roman L
  • 3,006
  • 25
  • 37
  • And I don't find the reason to define this two specialization, why you have to distinguish between const reference and copy. And I think there isn't no way to distinguish them. – mattia.penati Feb 02 '11 at 15:18
  • @mattia.penati: This wasn't my intention, actually... – Roman L Feb 02 '11 at 15:29
  • Ok, now is clear. First, you can put `explicit` in front of copy constructor if you want prevent implicit copy in function call. Now you can define the function `f` as `template f(T const &)`. Otherwise use overload. No other way. – mattia.penati Feb 02 '11 at 15:40
  • @mattia.penati: I don't want to change the original templated function, only to make pass-by-reference work with the specialized function... – Roman L Feb 02 '11 at 15:42
  • Use overload. Otherwhise no other way. – mattia.penati Feb 02 '11 at 15:48

6 Answers6

3

You're conflating three issues: templates, overloading and argument passing.

Just remove the specializations and pass the argument as T const&.

Cheers & hth.,

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
2

Why don't you overload:

void f(const C& c) { cout << "f(const C&)" << endl; }
Tomek
  • 4,554
  • 1
  • 19
  • 19
2

This would work:

int main()
{
    C c;
    f<const C&>(c);
    return 0;
}

Your alternative:

template<typename T> void f(const boost::reference_wrapper<T const>& c) 
    { cout << "f<boost_const_ref&>" << endl; } 

int main()
{
    C c;
    f(boost::cref(c));
    return 0;
}

In reality you would use boost::reference_wrapper to pass the reference through to where you want to use it. You can use get() to do that, although boost::reference_wrapper has an implicit conversion back to the reference, so you could probably get by without the partial-specialisation of the template and just passing boost::cref(c) to the regular one.

CashCow
  • 30,981
  • 5
  • 61
  • 92
1

So if you don't always want to accept a const reference (which is reasonable for base types [int, long, float etc.]), you can use a little boost magic.

#include <iostream>
#include <boost/call_traits.hpp>

using namespace std;

struct C
{
  C() { cout << "C()" << endl;  }
  C(const C&) { cout << "C(C)" << endl; }
  //C& operator=(C const&) { cout << "C=C" << endl; return *this; }
  ~C() { cout << "~C()" << endl;  }
};

template<class T> void foo(typename boost::call_traits<T>::param_type inst) { cout << "f<T>" << endl; }
// specialization for calling class C
template<> void foo<C>(boost::call_traits<C>::param_type inst) { cout << "f<C>" << endl; }

int main()
{
  int i = 0;
  foo<int>(i);
  C c;
  foo<C>(c);
  return 0;
}
Nim
  • 33,299
  • 2
  • 62
  • 101
  • My templated function doesn't need to accept a const reference so it is `f`. But I want my specialization to accept `const SpecificObject&`, i.e. `f` but I can't find out how to specialize it this way. – Roman L Feb 02 '11 at 15:39
  • @7vies, that's the beauty of `call_traits`, it automagically deduces that for `SpecificObject` it must pass via a const reference, so the specialization above will get called. For other types, a generated `foo` will be called (with either by value or const reference) – Nim Feb 02 '11 at 15:42
  • @Nim: Ok, but why do you have `param_type` twice, shouldn't it be `const_reference` in the second case? EDIT: Ah I've read what `param_type` means, so I see now... – Roman L Feb 02 '11 at 15:46
  • @7vies, you can change it, I just left it as it is, it reduces to the same thing at compile time. – Nim Feb 02 '11 at 15:48
  • @Nim: Indeed, it does not call the copy constructor and does pass-by-reference without the issues that I have with the standard template specialization behavior. So would it be correct to say that `boost::call_traits` is also a kind of an improved approach to template specialization? – Roman L Feb 02 '11 at 15:57
  • 1
    @7vies, `call_traits` is generally useful when you want to allow the compiler to decide what is the best way to pass an argument (and normally this is very useful in class templates or function templates where you don't know what the type will be). It certainly assists where there is likely to be any ambiguity in parameter passing and let's the compiler make the correct decision. – Nim Feb 02 '11 at 16:20
  • Is there a way to have the compiler automatically deduce the template type parameter from the function argument's type? So, far the only way i found was to define 2 `foo` templates with an extra dummy `std::enable_if` non-type parameter. – Johan Boulé Jan 13 '20 at 00:57
1

Your problem is that the actual parameter c isn't const, so the main template is a better match because it doesn't need to add 'const' to the type. If you try functions that pass by value and by non-const reference, the compiler will tell you that it cannot resolve that difference.

Bo Persson
  • 90,663
  • 31
  • 146
  • 203
0
#include <iostream>

using namespace std;

struct C
{
  C() { cout << "C()" << endl;  }
  C(const C&) { cout << "C(C)" << endl; }
  ~C() { cout << "~C()" << endl;  }
};

template<class T> void f(const T&) { cout << "f<T>" << endl; }

int main()
{
    C c;
    f(c);
    return 0;
}

This does do what you would like, but you must then use a const ref for all values passed into the function. I do not know if this was what you were looking for.

daramarak
  • 6,115
  • 1
  • 31
  • 50
  • Ah, I see. My first thought is that this might give you problems as the pass by the exact type will always be preferred. But I might be wrong. – daramarak Feb 02 '11 at 15:31