12

Please notice C++03 is what I really need, but for knowledge sake, I would like to see some more pretty implementations in C++11 as well.

I need a template class

template <typename T>
class A {
private:
    T m_member;

public:
    A(T _member);    
    //... MORE STUFF
    void foo(T param);
};

I need:

1) If A is compiled with a value type (including pointers, which by themselves are passed by value):

Then I need A to look like this (exactly like above)

class A {
private:
    T m_member;

public:
    A(T _member);    
    //... MORE STUFF
    void foo(T param);
};

2) If A is compiled with a reference type (for example int&):

Then I need A to look like this:

class A{
private:
    T& m_member;

public:
    A(T& _member);    
    //... MORE STUFF
    void foo(T param); // still the same T, not T&
};

If I knew A received only ints, then i would be able to use specialization. But any type can be used by A's user:

main.cpp

A<int> a1;//1st version
A<int&> a2;//2nd version
A<B> a3;//1st version
A<B&> a4;//2nd version
A<C*> a5;//1st version
Or B
  • 1,675
  • 5
  • 20
  • 41

2 Answers2

3

As seen (correctly) in this thread Specializing function template for reference types, the remove_reference suggested here won't work. It just won't go into the second implementation EVER, because the compiler sees T& and T just the same.

Instead you could MANUALLY tell the compiler that it is now dealing with a reference type, using the same specialization trick

template<typename T, bool isReference>
class A {
};

template<typename T>
class A<T,false>{
private:
    T m_member;

public:
    A(T _member);    
    //... MORE STUFF
    void foo(T param);
}
/////////////////////////
template<typename T>
class A<T,true>{
private:
    T& m_member;

public:
    A(T& _member);    
    //... MORE STUFF
    void foo(T param);
}

If you want to extract some similar behavior and avoid the code dupelication this solution causes, you could easily extract that behavior to a Base Class<T>, and do

template<typename T,bool isReference>
class A : public BaseClass<T>{
}

and so on.

Usage would be

main.cpp

A<int,false> a1;//1st version
A<int&,true> a2;//2nd version
A<B,false> a3;//1st version
A<B&,true> a4;//2nd version
A<C*,false> a5;//1st version, as pointers are value types
Community
  • 1
  • 1
Gulzar
  • 23,452
  • 27
  • 113
  • 201
  • @MattMcNabb My actual question is weather or not there is a semantic difference between `int` and `int&`? defining `void foo(int a){cout<<"int";}` and `void foo(int& a){cout<<"int&";}` in the same file compiles. calling with `f(4);` will output `"int"`, but calling with `int b; f(b)` will give ambiguity to the compiler. same goes for `int& c = b`. If i define a template and a specialization of , would two distinct classes be created? could you show working code for that? (the question I linked to shows the opposite). – Gulzar Aug 25 '15 at 06:56
  • Yes, `A` and `A` are distinct classes. See my answer to this question for working code. – M.M Aug 25 '15 at 07:57
  • In the linked question, `f` and `f` are distinct functions, but he only ever calls `f`. The code `f(z);` would call `f`. – M.M Aug 25 '15 at 08:06
1

Note: Credit for ideas in this answer goes to n.m.

To make your idea work, you just need to make the function argument type for foo to always be a non-reference type. In C++11 there is a helper function template called std::remove_reference for that, but in C++03 that can easily be implemented. Sample code:

#include <iostream>

template<typename T> struct remove_reference      {typedef T type;};
template<typename T> struct remove_reference<T&>  {typedef T type;};

template<typename T>
class A{
private:
    T m_member;

public:
    A(T m): m_member(m) {}
    void foo(typename remove_reference<T>::type param) { param = 0; }
};

struct B { int x; B(int x): x(x) {} };
struct C {};

int main()
{
    int a = 1;
    A<int> a1(a);
    A<int&> a2(a);

    a1.foo(a); std::cout << a << std::endl;
    a2.foo(a); std::cout << a << std::endl;

    B b(1);
    A<B> b1(b);
    A<B&> b2(b);
    b1.foo(b); std::cout << b.x << std::endl;
    b2.foo(b); std::cout << b.x << std::endl;

    C c;
    A<C*> c1(&c);
    c1.foo(&c);
}

Output:

$ g++ -o r r.cc -std=c++03 && ./r
1
1
1
1

As you can see, the 1 output indicates that the argument was passed by value (and so param = 0 is changing a local copy, not the object given as argument).

Another way to verify that the code is working as intended would be to output the gcc extension __PRETTY_FUNCTION__ inside foo. The output for that line with the call a2.foo(a); is:

void A<T>::foo(typename remove_reference<T>::type) [with T = int&; typename remove_reference<T>::type = int]
M.M
  • 138,810
  • 21
  • 208
  • 365