When a pointer type is passed as argument to template parameter T, how do I declare a pointer to const type?
Both const T and T const become const pointer to type, whereas I need to declare a pointer to const type.
template<typename ValueType>
class TestClass {
public:
void TestMethod(const ValueType x) {
// When ValueType is int*,
// type of x is int * const;
// how do I declare x such that it is const int* ?
std::cout<<x;
}
};
You can create a trait that does it.
template <typename T>
struct add_inner_const { using type = const T; };
template <typename T>
struct add_inner_const<T*> { using type = const T*; };
template <typename T>
using add_inner_const_t = typename add_inner_const<T>::type;
template<typename ValueType>
class TestClass {
public:
void TestMethod(add_inner_const_t<ValueType> x) {
// When ValueType is int*, x is const int*
std::cout<<x;
}
};
This will transform int ** into int * const *, if you instead want that to be const int **, then you need a different specialisation
template <typename T>
struct add_inner_const<T*> { using type = add_inner_const_t<T>*; };
Related
I'd like to have a way to compare different data types that are internally represented by an array (e.g. a string and a vector of chars) using a common array reference type. Consider the following code:
template <typename T>
struct ArrayConstRef {
const T *data;
size_t length;
};
template <typename T>
bool operator==(ArrayConstRef<T> a, ArrayConstRef<T> b);
template <typename T>
class ContainerA {
public:
operator ArrayConstRef<T>() const;
explicit operator const T *() const;
};
template <typename T>
class ContainerB {
public:
operator ArrayConstRef<T>() const;
explicit operator const T *() const;
};
int main() {
if (ContainerA<int>() == ContainerB<int>()) // error - no matching operator==
printf("equals\n");
return 0;
}
The overloaded operator== isn't matched even though the implicit conversion is available. Interestingly, if I removed the explicit keywords, the compiler manages to convert both objects to pointers and do the comparison that way (which I don't want). Why does one implicit conversion work but not the other? Is there a way to make it work?
This can be solved using SFINAE and little changes in code of your classes.
#include <cstddef>
#include <cstdio>
#include <type_traits>
template <typename T>
struct ArrayConstRef {
const T *data;
size_t length;
};
// This is needed to override other template below
// using argument depended lookup
template <typename T>
bool operator==(ArrayConstRef<T> a, ArrayConstRef<T> b){
/* Provide your implementation */
return true;
}
template <
typename Left,
typename Right,
// Sfinae trick :^)
typename = std::enable_if_t<
std::is_constructible_v<ArrayConstRef<typename Left::ItemType>, const Left&>
&& std::is_constructible_v<ArrayConstRef<typename Right::ItemType>, const Right&>
&& std::is_same_v<typename Left::ItemType, typename Right::ItemType>
>
>
inline bool operator==(const Left& a, const Right& b){
using T = typename Left::ItemType;
return ArrayConstRef<T>(a) == ArrayConstRef<T>(b);
}
template <typename T>
class ContainerA {
public:
// Add type of element
using ItemType = T;
operator ArrayConstRef<T>() const;
explicit operator const T *() const;
};
template <typename T>
class ContainerB {
public:
// Add type of element
using ItemType = T;
operator ArrayConstRef<T>() const;
explicit operator const T *() const;
};
int main() {
if (ContainerA<int>() == ContainerB<int>()) // no error :)
printf("equals\n");
return 0;
}
Compiles well with GCC 11.2 -std=c++17.
If you can use C++20, it is better to use concepts for this.
Code below compiles with GCC 11.2 -std=c++20.
#include <cstddef>
#include <cstdio>
#include <type_traits>
template <typename T>
struct ArrayConstRef {
const T *data;
size_t length;
};
// This is needed to override other template below
// using argument depended lookup
template <typename T>
bool operator==(ArrayConstRef<T> a, ArrayConstRef<T> b){
/* Provide your implementation */
return true;
}
template <typename Container>
concept ConvertibleToArrayConstRef =
requires (const Container& a) {
ArrayConstRef<typename Container::ItemType>(a);
};
template <
ConvertibleToArrayConstRef Left,
ConvertibleToArrayConstRef Right
>
requires (std::is_same_v<
typename Left::ItemType,
typename Right::ItemType>
)
inline bool operator==(const Left& a, const Right& b){
using T = typename Left::ItemType;
return ArrayConstRef<T>(a) == ArrayConstRef<T>(b);
}
template <typename T>
class ContainerA {
public:
// Add type of element
using ItemType = T;
operator ArrayConstRef<T>() const;
explicit operator const T *() const;
};
template <typename T>
class ContainerB {
public:
// Add type of element
using ItemType = T;
operator ArrayConstRef<T>() const;
explicit operator const T *() const;
};
int main() {
if (ContainerA<int>() == ContainerB<int>()) // no error :)
printf("equals\n");
return 0;
}
The problem is that operator== is a template, for it to be called the template parameter T needs to be deduced. But implicit conversion (ContainerA<int> -> ArrayConstRef<int> and ContainerB<int> -> ArrayConstRef<int>) won't be considered in template argument deduction.
Type deduction does not consider implicit conversions (other than type adjustments listed above): that's the job for overload resolution, which happens later.
On the other hand, if you specify the template argument to bypass the deduction (in a ugly style), the code works.
if (operator==<int>(ContainerA<int>(), ContainerB<int>()))
Another workaround, you might change the parameter type and add a helper like:
template <typename T>
bool equal_impl(ArrayConstRef<T> a, ArrayConstRef<T> b);
template <template <typename> class C1, template <typename> class C2, typename T>
std::enable_if_t<std::is_convertible_v<C1<T>, ArrayConstRef<T>>
&& std::is_convertible_v<C2<T>, ArrayConstRef<T>>,
bool>
operator==(C1<T> a, C2<T> b) {
return equal_impl<T>(a, b);
}
LIVE
How is it possible to have a templated class here called
FrontBackBuffer with template parameter TBackBufferType, TFrontBufferType
template< typename TBufferTypeFront, typename TBufferTypeBack = TBufferTypeFront>
class FrontBackBuffer{
public:
explicit FrontBackBuffer(
TBufferTypeFront const & m_front,
TBufferTypeBack const & m_back):
m_Front(m_front),
m_Back(m_back)
{
};
~FrontBackBuffer()
{};
typename std::remove_reference<
typename std::remove_pointer<TBufferTypeFront>::type
>::type & getFront(){return m_Front;} // error: invalid initialization of reference of type 'A&' from expression of type 'A*'| (here T is A)
typename std::remove_reference<
typename std::remove_pointer<TBufferTypeBack>::type
>::type & getBack(){return m_Back;}
TBufferTypeFront m_Front; ///< The front buffer
TBufferTypeBack m_Back; ///< The back buffer
};
I would like to achieve the following:
to be consistent in the code, I would prefere, to no matter what the Type inside the buffer is, to have a function getFront/Back which should always return a Reference to the buffer (either a const or a non-const depending on the type: e.g const int = T should return a const int & reference! I would like to write code like this
FrontBuffer<const int&, std::vector<int> > a;
a.getFront() = 4 //COmpile error! OK!;
a.getBack()[0] = 4;
FrontBuffer< int*, GAGAType * > b;
b.getBack() = GAGAType();
b.getFront() = int(4); // this is no ERROR, i would like to get the reference of the memory location pointet by int* ....
I would like this because I want to avoid changing the syntax if I change the buffer type from reference to pointer (where I need to dereference)
Is such a Buffer class possible to accept with all possible types (like shared_ptr)
asd
All I want is some access and it should be very performant, no copies and so on
I dont know really how to write this generic buffer? Somebody has any clue?
Thanks!!!
EDIT1 I want also to be able to assign to the dereferenced pointer:
b.getFront() = int(4); // this is no ERROR, i would like to get the reference of the memory location pointet by int* ....
Thats where my problem with traits comes in!
You need to specialize (traits technique) part of your template, like this:
template <typename T>
struct MyRefTypes {
typedef const T & Con;
typedef T& Ref;
typedef const T& CRef;
static Ref getRef(T& v) {
return v;
}
};
Update
Note the special function for returning reference - it is needed if you want to behave differently for pointers - returns references for it.
End Update
And make specialization for references and const references:
template <typename T>
struct MyRefTypes {
typedef const T & Con;
typedef T& Ref;
typedef const T& CRef;
static Ref getRef(T& v) {
return v;
}
};
//Specialization for Reference
template <typename T>
struct MyRefTypes<T&> {
typedef T & Con;
typedef T& Ref;
typedef const T& CRef;
static inline Ref getRef(T& v) {
return v;
}
};
//Specialization for const Reference
template <typename T>
struct MyRefTypes<const T&> {
typedef const T & Con;
typedef const T& Ref;
typedef const T& CRef;
static inline Ref getRef(const T& v) {
return v;
}
};
//Specialization for const
template <typename T>
struct MyRefTypes<const T> {
typedef const T & Con;
typedef const T& Ref;
typedef const T& CRef;
static inline Ref getRef(const T& v) {
return v;
}
};
Update
And this "special" specialization for pointers - so they will work as references:
//Specialization for pointers
template <typename T>
struct MyRefTypes<T*> {
typedef T* Con;
typedef T& Ref;
typedef T* const CRef; //! note this is a pointer....
static inline Ref getRef(T* v) {
return *v;
}
};
//Specialization for const pointers
template <typename T>
struct MyRefTypes<const T*> {
typedef const T* Con;
typedef const T& Ref;
typedef const T* const CRef; //! note this is a pointer....
static inline Ref getRef(const T* v) {
return *v;
}
};
((However I am not sure this specialization for pointers is a good design... ))
End Update
And usage inside your class template:
template< typename TBufferTypeFront, typename TBufferTypeBack = TBufferTypeFront>
class FrontBackBuffer{
public:
typedef typename MyRefTypes<TBufferTypeFront>::Ref TBufferTypeFrontRef;
typedef typename MyRefTypes<TBufferTypeFront>::CRef TBufferTypeFrontCRef;
typedef typename MyRefTypes<TBufferTypeFront>::Con TBufferTypeFrontCon;
typedef typename MyRefTypes<TBufferTypeBack >::Ref TBufferTypeBackRef;
typedef typename MyRefTypes<TBufferTypeBack >::CRef TBufferTypeBackCRef;
typedef typename MyRefTypes<TBufferTypeBack >::Con TBufferTypeBackCon;
explicit FrontBackBuffer(
TBufferTypeFrontCon m_front,
TBufferTypeBackCon m_back):
m_Front(m_front),
m_Back(m_back)
{
};
~FrontBackBuffer()
{};
// See here special functions from traits are used:
TBufferTypeFrontRef getFront(){return MyRefTypes<TBufferTypeFront>::getRef(m_Front); }
TBufferTypeBackRef getBack(){return MyRefTypes<TBufferTypeBack>::getRef(m_Back); }
TBufferTypeFront m_Front; ///< The front buffer
TBufferTypeBack m_Back; ///< The back buffer
};
It works as expected:
http://ideone.com/e7xfoN
I have template with overloaded method. I'm trying to create pointer to the overloaded method.
template<typename T>
class Future {
public:
const T& get() const;
bool get(T*, int timeoutMs) const;
};
...
const void*&(Future<void*>::*x)()const = &Future<void*>::get;
Compilation fails with this error:
no matches converting function 'get' to type 'const void*& (class Future<void*>::*)()const'
candidates are: const T& Future<T>::get() const [with T = void*]
bool Future<T>::get(T*, int) const [with T = void*]
I have tried to typedef Future<void*> without any luck.
If T is void* the const should be on the pointer not on the pointed memory:
void* const & (Future<void*>::*x)() const = &Future<void*>::get;
Templates themselves have no pointers. A template is a template, it tells c++ how to create a class. So
template<class T>
class Container {
public:
T* element() { return element_; }
void SetElement(Element *element) { element_ = element; }
typedef void (*SetElementFunctionPointer)(Element *element);
private:
T *element_;
};
typedef Container<int> IntContainer;
The code above shows you a Container template, and how you create an integer container (IntContainer) by using the Container template. In there I've declared a typedef for a SetElementFunctionPointer.
I have a class template that contains two similar member functions:
template<class T>
class MyTemplate {
// other stuff, then
static T* member( T& ); //line 100
static const T* member( const T& ); //line 101
};
which I instantiate like this:
MyTemplate<int* const>
and Visual C++ 9 complains:
mytemplate.h(101) : error C2535: 'int* MyTemplate<T>::member(T &)' :
member function already defined or declared
with
[
T=int *const
]
mytemplate.h(100) : see declaration of 'MyTemplate::member'
with
[
T=int *const
]
somefile.cpp(line) : see reference to class template instantiation
'MyTemplate<T>' being compiled
with
[
T=int *const
]
I certainly need the two versions of member() - one for const reference and one for non-const reference. I guess the problem has something with top-level const qualifiers, but can't deduce how to solve it.
How do I resolve this problem so that I still have two versions of member() and the template compiles?
The explanation given by fefe is correct. Foo const& and Foo const const& will simply evaluate to the same type, hence your function overloading does not work. In case your template argument is const, I'd suggest specialisation.
Version A:
template<class T>
class MyTemplate {
static T* member( T& );
static const T* member( const T& );
};
template<class T>
class MyTemplate<T const> {
static const T* member( const T& );
};
Version B:
template<class T>
class MyTemplate_mutableImpl {
static T* member( T& );
};
template<class T>
class MyTemplate_constImpl {
static const T* member( const T& );
};
template<class T>
class MyTemplate : public MyTemplate_mutableImpl<T>, public MyTemplate_constImpl<T> {
};
template<class T>
class MyTemplate<T const> : public MyTemplate_constImpl<T const> {
};
When T is int * const, T is already const, so T& and const T& are both int * const.
Or do you mean in this case, you need your class to look like:
class MyTemplate_int_p_const{
static int * member (int *&);
static int * const member (int * const &);
};
You can add this to your main template to achieve this:
template<class T>
class MyTemplate<const T>
{
static T * member(T&);
static const T* member(const T&);
};
As a responds to the OP's comment, if you don't want to use partial specialization, you'll need type_traits. It is supported by C++0x, and for VC++9, you can use boost.
In the following code, the non_const version of member will take a dummy_type ( a pointer to member function) if T is already const. So the non_const overload would not exist.
#include <type_traits>
template<class T>
class MyTemplate {
// other stuff, then
//void dummy(void);
typedef void (*dummy_type)(void);
typedef typename std::conditional<std::is_const<T>::value, dummy_type, T>::type T_no_const;
typedef typename std::remove_const<T>::type T_remove_const;
static T_no_const* member( T_no_const& t ) //line 100
{
if (std::is_same<T, T_no_const>::value)
{
return member_portal(t);
}
else
return NULL;
}
static T_no_const* member_portal(dummy_type&){return NULL;};
static T_remove_const* member_portal(T_remove_const&);
static const T* member( const T& ); //line 101
};
int main()
{
MyTemplate<int * const> mt;
MyTemplate<int *> mtt;
return 0;
}
This is the first time that I play with type_traits. It can pass compilation under g++ 4.5.2 with C++0x enabled. But I've never run it.
Main idea is, when T is const, the non_const version of member takes a argument of an arbitrary type ( a type that is not likely to be used any where else, and to not likely to be implicitly converted to), thus the non_const version disappears. But in the way, the logic breaks in the implementation of member ( as the argument type is to be used, but is not expected). So the main logic of member is move another function of member_portal.
A simple solution would be to disable the function if T is const:
#include <boost/mpl/if.hpp>
#include <boost/type_traits/is_const.hpp>
template<class T>
class MyTemplate {
static T* member( T& );
struct Disabled {};
static const T* member(typename boost::mpl::if_<boost::is_const<T>, Disabled, const T&>::type);
};
The following code compiles and works on G++ 4.4.0 and MS VC2008 Express.
#include <iostream>
template<typename T> struct A{
protected:
T v;
public:
const T get() const{
return v;
}
A(T v_)
:v(v_){
}
};
class B: public A<const int*>{
public:
void doSomething() const{
const int* tmp = get();
std::cout << *tmp << std::endl;
}
B(const int* p)
:A<const int*>(p){
}
};
int main(int argc, char** argv){
int a = 134;
B b(&a);
const B& c = b;
b.doSomething();
c.doSomething();
return 0;
}
However, as I understand it using A<const int*> should result in const const int* A::get() const;. And I'm pretty sure I haven't seen anything like that in real code. Is using template in such way "legal"?
If not, what are alternatives? In my code I need a template class that provides two "getter" methods (const/non-const), and can take a "const int*" as a type. Something like this:
template<typename T> struct A{
protected:
T v;
public:
const T get() const{
return v;
}
T get(){
return v;
}
A(T v_)
:v(v_){
}
};
Any ideas?
If T = const int *, then const T is const int * const.
We can translate
const int *
into
int const *
A pointer to a constant int.
When you have T=int*, what you have with const T is:
int * const
Which means, a constant pointer to a int.
So in your case with T=const int*, which is int const *, what you have with const T is:
int const * const
Which means a constant pointer to a constant int. Which is legal.
If it is not the desired behavior, you may have partial specialization, like:
template<typename T> struct A<const T>{
//here comes different implementation for the case where A template is
//initialized with a const type.
It is not an issue to have multiple const qualifiers: they just fold together.
However you are misinterpreting it, because you are not placing it correctly (and it's a shame the syntax allows it).
If you place the const after the type it qualifies you'll realize that you were reading it wrong.
A const T where T is const int* is NOT const const int* = const int*.
If you write it correctly: T const where T is int const*, then you will read it int const* const, which is a const pointer to a const int, and NOT a pointer to a const int.
It's fine to do this
struct Foo {};
typedef const Foo CFoo;
const CFoo c;
All the other answers stand about constness, if you wanted the unqualified type, you can use a little template skull-duggery..
template <typename T>
struct remove_const
{
typedef T type;
};
template <typename T>
struct remove_const<T*>
{
typedef T* type;
};
template <typename T>
struct remove_const<const T>
{
typedef T type;
};
template <typename T>
struct remove_const<const T*>
{
typedef T* type;
};
template <typename T>
void foo (T& b)
{
typename remove_const<T>::type a = b;
}
If you pass in a const pointer to foo, you'll see that a has the non-const type and therefore the assignment fails.
const int const * is legal code and means a constant pointer to a constant value. Your code probably translates to the same expression.