C ++ template with 'const'

Consider the following class of templates:

template <typename T> class Function { public: virtual float eval( const T &x, const T &y ) = 0; }; 

Since the eval function should not change the value of the two inputs "x" and "y", I put them as "const". Then I create the next class derived from the function

 class Foo1 : public Function <float*> { public: Foo1() : Function <float*> () {} virtual float eval( const float* &x, const float* &y ) { ... } }; 

When I compile with g ++, I get the following warning:

 hidden overloaded virtual function 'Function<float *>::eval' declared here: type mismatch at 1st parameter ('float *const &' vs 'const float *&') virtual float eval( const T &x, const T &y ) = 0; 

And I can not instantiate the class Foo1. The compiler says that the eval function is not implemented. To make the compiler happy, the derived class should be as follows:

 class Foo2 : public Function <float*> { public: Foo2() : Function <float*> () {} virtual float eval( float* const &x, float* const &y ) { ... } }; 

The Foo2 :: eval function uses two parameters of type "float * const" instead of "const float *". In other words, Foo2 :: eval can modify the contents of the arrays "x" and "y". This is not what I want.

I tried to change the template class "Function" as follows:

  virtual float eval( T const &x, T const &y ) = 0; 

But the class Foo1 still does not work, the class Foo2 works the same as in the previous case.

  • So it seems that either "const T & x" or "T const & x" in the template class implies "float * const & x" in the derived class. Is it correct?
  • If I want 'const float * & x' (or 'const float * x') in a derived class, what should my Template function class be?

Thanks.

+6
source share
3 answers

The behavior you observe is 100% correct with respect to the standard. This is a classic example of a "constant pointer" and a "pointer to a constant."

Your main template declares that it accepts a "reference to T through which it cannot change the object of the T to which it refers" (syntax const T & , equivalent to T const & ).

Then you create an instance of the template with type float* , that is, a pointer to float . β€œFrom this it follows that the type of the function parameter after the substitution of the template parameter is really a reference to float * through which it cannot change the beginning of float * . There is no way to smuggle" cannot change the float to which float * refers to points "directly there.

I see two options. First, if this kind of use is the only use of T in Function , just use const float * as the template argument, since you really want T :

 class Foo1 : public Function <const float*> { public: Foo1() : Function <const float*> () {} virtual float eval( const float* const &x, const float* const &y ) { ... } // notice two `const` keywords above: one for the pointed object, one for the reference }; 

If this is not an option for you (i.e. you need float * somewhere inside Function and const float* elsewhere), you will have to use some type traits and change the eval parameters. Something like that:

 template <class T> struct unmodifiable { typedef const T &type; }; template <class T> struct unmodifiable<T*> { typedef const T* const &type; // if you want to keep the reference // or just: // typedef const T *type; // if you don't want to bother with the reference for pointer types }; template <typename T> class Function { public: virtual float eval( typename unmodifiable<T>::type x, typename unmodifiable<T>::type y ) = 0; }; class Foo1 : public Function <float*> { public: Foo1() : Function <float*> () {} virtual float eval( unmodifiable<float*>::type x, unmodifiable<float*>::type y ) { ... } // or just spell it out exactly, based on the variant of `unmodifiable` you've chosen, eg: // virtual float eval (const float *x, const float *y) { ... } }; 
+8
source

It seems that either const T &x or T const &x in the template class implies float* const &x in the derived class. Is it correct?

Yes this is correct. The way to think about it is that T always const ; in your case, T turns out to be a pointer.

If I want const float* &x (or const float* x ) in a derived class, what should my Template function class be like?

It looks like it should be class Foo2 : public Function <const float*> .

+1
source

The solution is to partially specialize Function for pointer types in order to use a different signature for eval :

  template <typename T> class Function<T*> { public: virtual float eval( const T* x, const T* y ) = 0; }; 

Alternatively, you can get Foo2 from Function<const float*> .

0
source

Source: https://habr.com/ru/post/975175/


All Articles