Using templates to set whether class members are const - c++

I have a templated class and I'd like to almost toggle whether things within it are const based on the template type.
pseudocode:
template<bool isConst>
Class ConstableClass
{
public:
// if isConst == true make this method const
void DoSomething() "const";
// if isConst == true make the returned smartpointer type const method const
std::unique_ptr<"const" int> operator->() "const";
private:
// if isConst == true add const at the start of the next line
"const" int foo;
}
Is this sort of thing possible?

With type-traits and SFINAE:
template <bool isConst>
class ConstableClass
{
public:
template <bool C = isConst, typename = std::enable_if_t<C>>
void DoSomething() const;
template <bool C = isConst, typename = std::enable_if_t<!C>>
void DoSomething();
template <bool C = isConst, typename = std::enable_if_t<C>>
std::unique_ptr<const int> operator->();
template <bool C = isConst, typename = std::enable_if_t<!C>>
std::unique_ptr<int> operator->();
private:
std::conditional_t<isConst, const int, int> foo{};
};
With a specialization:
template <bool isConst>
class ConstableClass
{
public:
void DoSomething() const;
std::unique_ptr<const int> operator->() const;
protected:
const int foo{};
};
template <>
class ConstableClass<false>
{
public:
void DoSomething();
std::unique_ptr<int> operator->();
protected:
int foo{};
};
With constraints in c++20:
template <bool isConst>
class ConstableClass
{
public:
void DoSomething() const requires isConst;
void DoSomething() requires not isConst;
std::unique_ptr<const int> operator->() const requires isConst;
std::unique_ptr<int> operator->() requires not isConst;
private:
std::conditional_t<isConst, const int, int> foo{};
};

The first use case is not possible. You can't make function modifiers conditional and based on some template. However, I imagine you are trying to do this because you don't want to copy&paste code between the const and regular version of a function. In that case, just write a private impl method which does the actual work and use it in the cost and non-const version of the class.
private:
int& get_impl() const {...}
public:
const int& get() const {return get_impl();}
int& get() {return get_impl();}
The rest is possible and quite simple:
std::unique_ptr<"const" int>
// we can do this by:
std::unique_ptr<std::conditional_t<isConst, const int, int>> ...
// this can be written more elegantly as
template <typename T, bool isConst>
using const_if_t = std::conditional_t<isConst, const T, T>;
std::unique_ptr<const_if_t<int, isConst>>;
Making member variables const conditionally would be pretty pointless because this already happens when you make a variable const anyways.

No, it is not possible the way you want to do it. While there is a way to manipulate member variable types and function return types, there is no way to add/remove const specifier to a method. This means you have to write 2 separate specializations of the class definition for the true and false values of the template parameter.

I have a templated class and I'd like to almost toggle whether things within it are const based on the template type. Is this sort of thing possible?
Yes, and I find specializations to be more readable (unless you have access to concepts). The example below shows, rather explicitly, the two cases.
template<bool isConst>
class ConstableClass;
template<>
class ConstableClass<false>
{
int foo;
public:
void DoSomething();
std::unique_ptr<int> operator->();
};
template<>
class ConstableClass<true>
{
int const foo;
public:
ConstableClass()
: foo{0} {} // example
void DoSomething() const;
std::unique_ptr<int const> operator->() const;
};
Keep in mind that you do need to add a constructor for the true case, as adding const to foo implicitly deletes it.

Related

C++ template class operator overloading with the same signatures

A simple C++ OO question regrading templates and operator overloading: In the following class, I have overloaded the index operator twice:
template<class A, class B>
class test
{
A a1;
B a2;
public:
A& operator[](const B&);
B& operator[](const A&);
};
Now, if I instantiate an object of this template class with the same typenames:
test<int, int> obj;
calling the index operator will result in an error, because the two overloaded functions will have the same signatures.
Is there any way to resolve this issue?
Sorry, if this is a basic question. I am still learning!
You can add a partial specialization:
template<class A>
class test<A, A>
{
A a1, a2;
public:
A& operator[](const A&);
};
You can avoid this issue and make the code more robust and expressive by converting the index to some other type that clarifies what the user wants. Usage would be like this:
bidirectional_map<int, int> myTest;
int& valueFor1 = myTest[Key{1}];
int& key1 = myTest[Value{valueFor1}];
Implemented like this:
template<class TKey>
struct Key { const TKey& key; };
template<class TValue>
struct Value { const TValue& value; };
// Deduction guides (C++17), or use helper functions.
template<class TValue>
Value(const TValue&) -> Value<TValue>;
template<class TKey>
Key(const TKey&) -> Key<TKey>;
template<class TKey, class TValue>
class bidirectional_map
{
TKey a1; // Probably arrays
TValue a2; // or so?
public:
TValue & operator[](Key<TKey> keyTag) { const TKey & key = keyTag.key; /* ... */ }
TKey & operator[](Value<TValue> valueTag) { const TValue& value = valueTag.value; /* ... */ }
};
Now, Key and Value are popular names so having them "taken up" by these auxiliary functions is not the best. Also, this is all just a pretty theoretical exercise, because member functions are of course a much better fit for this task:
template<class TKey, class TValue>
class bidirectional_map
{
TKey a1; // Probably arrays
TValue a2; // or so?
public:
TValue& getValueForKey(const TKey& key) { /* ... */ }
TKey& getKeyForValue(const TValue& value) { /* ... */ }
};
In C++2a, you might use requires to "discard" the function in some case:
template<class A, class B>
class test
{
A a1;
B a2;
public:
A& operator[](const B&);
B& operator[](const A&) requires (!std::is_same<A, B>::value);
};
Demo
Here is an example solution using if constexpr that requires C++17:
#include <type_traits>
#include <cassert>
#include <string>
template <class A, class B>
class test
{
A a1_;
B b1_;
public:
template<typename T>
T& operator[](const T& t)
{
constexpr bool AequalsB = std::is_same<A,B>();
constexpr bool TequalsA = std::is_same<T,A>();
if constexpr (AequalsB)
{
if constexpr (TequalsA)
return a1_; // Can also be b1_, same types;
static_assert(TequalsA, "If A=B, then T=A=B, otherwise type T is not available.");
}
if constexpr (! AequalsB)
{
constexpr bool TequalsB = std::is_same<T,B>();
if constexpr (TequalsA)
return a1_;
if constexpr (TequalsB)
return b1_;
static_assert((TequalsA || TequalsB), "If A!=B, then T=A || T=B, otherwise type T is not available.");
}
}
};
using namespace std;
int main()
{
int x = 0;
double y = 3.14;
string s = "whatever";
test<int, int> o;
o[x];
//o[y]; // Fails, as expected.
//o[s]; // Fails, as expected
test<double, int> t;
t[x];
t[y];
//t[s]; // Fails, as expected.
return 0;
};

How to keep track of Eigen objects through a solve()?

This question is related to cast from Eigen::CwiseBinaryOp to MatrixXd causes segfault .
It will probably have as simple a solution as the former.
In this minimal example, I define Holder, which holds and Eigen matrix, and returns it via its get() member function. Similarly, Decomp is an expression template for the LDLT decomposition of this matrix, and Solve solves for AX=B, yielding X.
#include <Eigen/Dense>
#include <Eigen/Cholesky>
template <class EigenType> class Holder {
public:
typedef EigenType result_type;
private:
result_type in_;
public:
Holder(const EigenType& in) : in_(in) {}
const result_type& get() const { return in_; }
};
template <class Hold> class Decomp {
public:
typedef typename Eigen::LDLT
<typename Eigen::MatrixBase<typename Hold::result_type>::PlainObject>
result_type;
private:
Hold mat_;
public:
Decomp(const Hold& mat) : mat_(mat) {}
result_type get() const { return mat_.get().ldlt(); }
};
template <class Derived, class OtherDerived> class Solve {
public:
typedef typename Eigen::internal::solve_retval
<typename Derived::result_type, typename OtherDerived::result_type>
result_type;
private:
Derived decomp_;
// typename Derived::result_type decomp_;
OtherDerived mat_;
public:
Solve(const Derived& decomp, const OtherDerived& mat)
: decomp_(decomp), mat_(mat) {}
//: decomp_(decomp.get()), mat_(mat) {}
result_type get() const { return decomp_.get().solve(mat_.get()); }
// result_type get() const { return decomp_.solve(mat_.get()); }
};
typedef Holder<Eigen::MatrixXd> MatrixHolder;
typedef Decomp<MatrixHolder> MatrixDecomp;
typedef Solve<MatrixDecomp, MatrixHolder> SimpleSolve;
The following test fails on X.get()
#include "Simple.h"
#include <Eigen/Dense>
#include <iostream>
int main(int, char * []) {
MatrixHolder A(Eigen::MatrixXd::Identity(3, 3));
MatrixHolder B(Eigen::MatrixXd::Random(3, 2));
MatrixDecomp ldlt(A);
SimpleSolve X(ldlt, B);
std::cout << X.get() << std::endl;
return 0;
}
but if you use the commented out lines in the header file, everything works fine. Unfortunately, this moves the evaluation of the decomposition to the construction of the solver, which is not suited to my use. Typically, I want to build a complicated expression expr involving this Solve, and call expr.get() later on.
How can I solve this problem? Is there a general rule to follow, to avoid further related questions?
To avoid useless and expensive copies, the internal solve_retval structure stores the decomposition and right-hand-side by const references. However, the LDLT object created in the Decomp::get function is deleted at the same time this function returns, and thus the solve_retval object references dead objects.
One possible workaround, is to add a Decomp::result_type object in Solve, and initialize it in Solve::get. Moreover, to avoid multiple deep copies, I suggest to use const references for a few attributes as follow:
#include <Eigen/Dense>
#include <Eigen/Cholesky>
template <class EigenType> class Holder {
public:
typedef EigenType result_type;
private:
result_type in_;
public:
Holder(const EigenType& in) : in_(in) {}
const result_type& get() const { return in_; }
};
template <class Hold> class Decomp {
public:
typedef typename Eigen::LDLT
<typename Eigen::MatrixBase<typename Hold::result_type>::PlainObject>
result_type;
private:
const Hold& mat_;
mutable result_type result_;
mutable bool init_;
public:
Decomp(const Hold& mat) : mat_(mat), init_(false) {}
const result_type& get() const {
if(!init_) {
init_ = true;
result_.compute(mat_.get());
return result_;
}
}
};
template <class Derived, class OtherDerived> class Solve {
public:
typedef typename Eigen::internal::solve_retval
<typename Derived::result_type, typename OtherDerived::result_type>
result_type;
private:
const Derived& decomp_;
const OtherDerived& mat_;
public:
Solve(const Derived& decomp, const OtherDerived& mat)
: decomp_(decomp), mat_(mat) {}
result_type get() const {
return decomp_.get().solve(mat_.get());
}
};
The general rule is to store heavy objects by const reference (to avoid deep copies) and lightweight expressions by value (to reduce temporary life issues).

How to resolve this issue with top-level const?

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);
};

"const const T" in template

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.

Using STL algorithms (specifically std::sort) from within a templated class

I've declared a template class MyContainer as bellow, then created an instance of it of type DataType1. The DataType1 class provides a friend function "DataSpecificComparison" which is used by std::sort to compare DataType1 objects. The program compiled and sorted correctly.
I then defined a class called DataType2, gave it a friend implementation of "DataSpecificComparison" and used it to create another instance of MyContainer.
I am now unable to compile the program as a "C2914: 'std::sort' : cannot deduce template argument as function argument is ambiguous" compile time error is reported.
How can a developer specify that the DataSpecificComparison binary predicate is to take arguments of template type T*? Or is there another way around this issue?
template <class T>
class MyContainer
{
private:
vector<T*> m_vMyContainerObjects;
....
public:
....
void SortMyContainerObjects()
{
std::sort(m_vMyContainerObjects.begin(), m_vMyContainerObjects.end(), DataSpecificComparison)
}
}
class DataType1
{
....
friend bool DataSpecificComparison(const DataType1 * lhs, const DataType1 * rhs)
}
class DataType2
{
....
friend bool DataSpecificComparison(const DataType2* lhs, const DataType2* rhs)
}
You can use a temporary local function pointer variable of the required type to select the correct overload of DataSpecificComparison:
void SortMyContainerObjects()
{
typedef bool (*comparer_t)(const T*, const T*);
comparer_t cmp = &DataSpecificComparison;
std::sort(m_vMyContainerObjects.begin(), m_vMyContainerObjects.end(), cmp);
}
Here the compiler can deduce that you want to use the DataSpecificComparison overload that matches the comparer_t type, which resolves the ambiguity.
sth already gave a correct answer, but there's also a direct alternative based on the same principle:
void SortMyContainerObjects()
{
std::sort(m_vMyContainerObjects.begin(), m_vMyContainerObjects.end(),
static_cast<bool (*comparer_t)(const T*, const T*)>(&DataSpecificComparison));
}
This uses essentially the same mechanism. The cast forces overload resolution to happen before the Template Argument Deduction for std::sort.
template<typename T>
struct DataSpecificComp : public binary_function<T, T, bool>
{
public:
bool operator()(const T* lhs, const T* rhs)
{
return *lhs < *rhs;
}
};
call the sort function as shown below:
sort(vi.begin(), vi.end(), DataSpecificComp<int>());
I'd prefer something along the following lines: by default it compares objects with less_than (so you wouldn't have to remember to provide a function with a funny name), and there's an overload that allows giving your own comparison functor (again, value-based):
#include <vector>
#include <algorithm>
#include <functional>
template <class T, class Func>
struct indirect_binary_call_type: public std::binary_function<const T*, const T*, bool>
{
Func f;
indirect_binary_call_type(Func f): f(f) {}
bool operator()(const T* a, const T* b) const
{
return f(*a, *b);
}
};
template <class T, class Func>
indirect_binary_call_type<T, Func> indirect_binary_call(Func f)
{
return indirect_binary_call_type<T, Func>(f);
}
template <class T>
class MyContainer
{
private:
std::vector<T*> m_vMyContainerObjects;
public:
void Sort()
{
Sort(std::less<T>());
}
template <class Func>
void Sort(Func f )
{
std::sort(m_vMyContainerObjects.begin(), m_vMyContainerObjects.end(), indirect_binary_call<T>(f));
}
};
int main()
{
MyContainer<int> m;
m.Sort();
m.Sort(std::greater<int>());
}
Did you try defining DataSpecificComparison as template with bunch of specializations and giving it the type?
template<T>
bool DataSpecificComparison(const T* t1, const T* t2)
{
// something non compilable here
}
template<> bool DataSpecificComparison<Data1>(const Data1* t1, const Data1* t2)
{
// return *t1 < *t2;
}
....
void SortMyContainerObjects()
{
std::sort(m_vMyContainerObjects.begin(), m_vMyContainerObjects.end(), DataSpecificComparison<T>)
}
....
Templating DataSpecificComparison should work. You can also specifically call the proper std::sort template, but it's a bit cumbersome:
template <class T>
class MyContainer
{
private:
vector<T*> m_vMyContainerObjects;
typedef bool (*compsT)(T, T);
public:
....
void SortMyContainerObjects()
{
std::sort<std::vector<T*>::iterator, compsT>(m_vMyContainerObjects.begin(), m_vMyContainerObjects.end(), DataSpecificComparison);
}
}