Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++: how to use a template on both classes and primitives?

I have a question about templates that can be used with parameters that are either a class or a primitive type. Here's some sample code:

(Note: I have real code that's more complex, the following code is useless but it reproduces the same issue)

template<typename T>
class Foo
{
  T value;

public:
  Foo() {}
  const T& getValue() const { return value; }
  Foo& setValue(const T& other) { 
    value = other; return *this; 
  }
};

struct Bar
{
  int x;

  Bar() : x(3) {}
};

int doit()
{
  Foo<int> fooint;
  Bar bar;
  bar.x = 44;
  Foo<Bar> foobar;

  fooint.setValue(3);      // warning here
  foobar.setValue(bar);

  int y = foobar.getValue().x + fooint.getValue();
  return y;
}

I get a compiler remark on fooint.setValue():

value copied to temporary, reference to temporary used

I understand the remark. What I'm wondering is how I should handle Foo::setValue() if I'm going to use Foo with both primitives and class/struct types as template parameters.

I thought setValue(const T& other) was the right method signature for passing in a constant class by reference.

Is there a way to make setValue() so it "does the right thing" both for Foo<int> and Foo<Bar>?

like image 955
Jason S Avatar asked Jun 03 '11 21:06

Jason S


3 Answers

It is perfectly legal to bind temporaries to const references as you do in setValue(). Intel C++, which issues this remark, is not being helpful in this case.

EDIT: I am guessing that TI compiler is based on Intel, which, for me, issues the following diagnostic on that line:

test.cc(28): remark #383: value copied to temporary, reference to temporary used
    fooint.setValue(3);      // warning here

The diagnostic is discussed on http://software.intel.com/en-us/articles/cdiag383/ where it says

Can safely ignore this warning for pushback function of vector. The vector copies the argument into its own storage; it never stores the original argument. Therefore, using a temporary is perfectly safe.

In your case, you're also copying the argument, therefore it can also be ignored.

like image 98
Cubbi Avatar answered Oct 23 '22 14:10

Cubbi


I see nothing wrong with your code. GCC compiles it without errors or warnings.

like image 1
HighCommander4 Avatar answered Oct 23 '22 14:10

HighCommander4


To answer your last question of getting Foo<T>::setValue() to do the 'right thing' you can consider using template specialization to accomplish that -- a fairly common technique used in template metaprogramming. Something like this for example:

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

template <typename T>
struct Ref_or_Value<T *>
{
    typedef T* type;
};

template <>
struct Ref_or_Value<int>
{
    typedef int type;
};

// add other primitive types like above as need

The Foo<T>::setValue signature then becomes:

Foo& setValue(const typename Ref_or_Value<T>::type other);

Whether this is overkill or not I'll let you decide but this should get setValue to do the 'right thing'. If T=int then setValue will take arguments by value. If T=Foobar_object then it will be by const reference.

like image 1
greatwolf Avatar answered Oct 23 '22 12:10

greatwolf