I have a class that wraps some type and attaches a Dimension. It should be convertible to the underlying type, but only if Dim=0. The conversion operator should not be callable in other cases (so a static_assert in the function would not work for me).
The following code works, if the enable_if-construction is deleted, but not in this form.
template<class T, int Dim>
class Unit {
public:
explicit Unit(T const& value): _value(value) {}
template<int D = Dim, typename = typename std::enable_if<D == 0>::type>
operator T() { return _value; }
private:
T _value;
};
auto main() -> int
{
auto a = double{0};
auto u = Unit<double, 0>{a};
auto i = static_cast<int>(u);
return i;
}
What is the reason for this and is there a work around to allow the cast, but also restrict the conversion?
As I understand, you want:
template <class T, int Dim>
class Unit {
public:
explicit Unit(T const& value): _value(value) {}
template <typename U, int D = Dim,
std::enable_if_t<D == 0 && std::is_convertible_v<T, U>, int> = 0>
operator U() { return _value; }
private:
T _value;
};
Demo
And in C++20, look nicer
template<class T, int Dim>
class Unit {
public:
explicit Unit(T const& value): _value(value) {}
template <typename U>
requires(Dim == 0 && std::is_convertible_v<T, U>)
operator U() const { return _value; }
private:
T _value;
};
Demo
Related
I'm trying to write a template class that will wrap a type that could be either a primitive (uint8_t up to float) or a char* string. If the class wraps a primitive type, setting its value to any primitive will do a straight cast, while setting a char* will to an ascii to primitive conversion (atoi, sprintf, etc). If it wraps a char*, the class should manage an internal char* buffer and convert primitive to char*.
If I limit my example to only uint32_t values, this is the template that would deal with any primitive type (with obvious error checking details omitted):
template<typename T>
class Measurement
{
public:
Measurement();
~Measurement();
void setU32Value(uint32_t value);
void setStringValue(const char* std);
uint32_t asU32() const;
const char* asString() const;
private:
T _value;
};
template<typename T>
void Measurement<T>::setU32Value(uint32_t value)
{
_value = (T)value;
}
template<typename T>
void Measurement<T>::setStringValue(const char* value)
{
_value = (T)::atoi(value);
}
template<typename T>
uint32_t Measurement<T>::asU32() const
{
return (uint32_t)_value;
}
template<typename T>
const char* Measurement<T>::asString() const
{
ostringstream oss;
oss << _value;
return oss.str();
}
But it won't work if I try to Measurement<char*> because it won't convert setU32Value wont' convert "value" to a string representation and the asU32() won't convert it back.
Can anyone suggest a way to make this work?
Thanks.
Your class definitely should not manually manage the lifetime of its string. Please see the Single-responsibility principle, The rule of three/five/zero and RAII. So the string storage type should be std::string and not char*.
C++20 concepts:
#include <string>
#include <concepts>
#include <type_traits>
template<typename T>
class Measurement
{
private:
T _value{};
public:
Measurement() = default;
template <std::integral U> requires std::integral<T>
void setIntValue(U value)
{
_value = static_cast<U>(value);
}
template <std::integral U> requires std::same_as<T, std::string>
void setIntValue(U value)
{
_value = std::to_string(value);
}
void setStringValue(const std::string& str) requires std::integral<T>
{
_value = static_cast<T>(std::stoll(str));
}
void setStringValue(std::string str) requires std::same_as<T, std::string>
{
_value = std::move(str);
}
template <std::integral U> requires std::integral<T>
U asInt() const
{
return static_cast<U>(_value);
}
template <std::integral U> requires std::same_as<T, std::string>
U asInt() const
{
return static_cast<U>(std::stoll(_value));
}
std::string asString() const requires std::integral<T>
{
return std::to_string(_value);
}
std::string asString() const requires std::same_as<T, std::string>
{
return _value;
}
};
Usage example:
auto test()
{
{
auto m = Measurement<long>{};
m.setIntValue<int>(24);
int a = m.asInt<int>();
std::string s = m.asString();
m.setStringValue("11");
a = m.asInt<int>();
s = m.asString();
}
{
auto m = Measurement<std::string>{};
m.setIntValue<int>(24);
int a = m.asInt<int>();
std::string s = m.asString();
m.setStringValue("11");
a = m.asInt<int>();
s = m.asString();
}
}
Template specialization
#include <string>
#include <type_traits>
template <class T, class Enable = void> class Measurement;
template <class T>
class Measurement<T, std::enable_if_t<std::is_integral_v<T>>>
{
private:
T _value{};
public:
Measurement() = default;
template <std::integral U>
void setIntValue(U value)
{
_value = static_cast<U>(value);
}
void setStringValue(const std::string& str)
{
_value = static_cast<T>(std::stoll(str));
}
template <std::integral U>
U asInt() const
{
return static_cast<U>(_value);
}
std::string asString() const requires std::integral<T>
{
return std::to_string(_value);
}
};
template <>
class Measurement<std::string>
{
private:
std::string _value{};
public:
Measurement() = default;
template <std::integral U>
void setIntValue(U value)
{
_value = std::to_string(value);
}
void setStringValue(std::string str)
{
_value = std::move(str);
}
template <std::integral U>
U asInt() const
{
return static_cast<U>(std::stoll(_value));
}
std::string asString() const
{
return _value;
}
};
Same usage as before.
I have to say that the class seems too bloated. I would instead have a class for storing the measurement and a separate utility to help convert between measurement and integers/string.
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;
};
I try to make a generic, but still efficient multi dimension Point class.
What I have is a Dimensions enum
enum Dimension : std::size_t { _2D = 2, _3D = 3 };
And a Point class
template <typename T, Dimension D>
class Point : public std::array<T, D>
{
public:
T& at(size_t idx) { return std::array<T,D>::at(idx); };
const T& at(size_t idx) const { return std::array<T,D>::at(idx); };
Dimension dim() const { return D; }
...
};
I would like to create nices constructors so I added (outside my class definition)
template <typename T>
Point<T,_2D>::Point(T x, T y) { at(0) = x; at(1) = y; }
template <typename T>
Point<T,_3D>::Point(T x, T y, T z) { at(0) = x; at(1) = y; at(2) = z; }
But still I cannot use thoses. The compiler tells me only default (empty) and copy constructors are registered.
Question:
How do I define constructors with arguments list length being dependant on my Dimension template ?
I would do it like this because I'm too lazy to write many versions of the same thing:
template<class T, Dimension D>
class Point : public std::array<T,D> {
template<class... Args>
Point(Args... vs) :
std::array<T,D>{{vs...}}
{
static_assert(sizeof...(Args) == D, "wrong number of args");
}
...
};
There are a few ways to accomplish this. In your case, it might be easiest to use std::enable_if:
template <typename T, Dimension D>
class Point : public std::array<T, D>
{
public:
template <
Dimension D2=D,
typename = typename std::enable_if<D2==_2D>::type
>
Point(T x,T y);
template <
Dimension D2=D,
typename = typename std::enable_if<D2==_3D>::type
>
Point(T x,T y,T z);
T& at(size_t idx) { return std::array<T,D>::at(idx); };
const T& at(size_t idx) const { return std::array<T,D>::at(idx); };
Dimension dim() const { return D; }
...
};
Another option would be to break this into multiple classes and use specialization:
template <typename T, Dimension D>
class PointBase : public std::array<T, D>
{
public:
T& at(size_t idx) { return std::array<T,D>::at(idx); };
const T& at(size_t idx) const { return std::array<T,D>::at(idx); };
Dimension dim() const { return D; }
...
};
template <typename T, Dimension D> class Point;
template <typename T>
class Point<T,_2D> : public PointBase<T,_2D> {
public:
Point(T x,T y);
};
template <typename T>
class Point<T,_3D> : public PointBase<T,_3D> {
public:
Point(T x,T y,T z);
};
I wrote a class and I wanted to implement an iterator for it ( as shown in the following code ). I needed to overload a variety of operators and I faced the error mentioned below:
class BaseClass
{
virtual ~BaseClass() {}
};
template<class T>
class AbstractBaseOrgan: public BaseClass
{
public:
typedef T value;
template<class TT>
class AbstractBaseIterator:
public std::iterator<std::random_access_iterator_tag,
typename std::iterator_traits<TT>::value_type>
{
protected:
TT _M_current;
const TT&
base() const
{ return this->_M_current; }
};
protected:
value te;
};
template<typename Iter>
inline bool
operator<(const typename AbstractBaseOrgan<typename
std::iterator_traits<Iter>::value_type>::template
AbstractBaseIterator<Iter>& lhs,
const typename AbstractBaseOrgan<typename
std::iterator_traits<Iter>::value_type>::template
AbstractBaseIterator<Iter>& rhs)
{ return lhs.base() < rhs.base(); }
int main()
{
AbstractBaseOrgan<int>::AbstractBaseIterator<int*> temp;
AbstractBaseOrgan<int>::AbstractBaseIterator<int*> temp2;
int ttemp;
if(operator< (temp,temp2))
ttemp = 0;
return 0;
}
Compiling it gives me the following error:
error: no matching function for call to ‘operator<(AbstractBaseOrgan<int>::AbstractBaseIterator<int*>&, AbstractBaseOrgan<int>::AbstractBaseIterator<int*>&)’
Any idea what might cause this?
4 In most cases, the types, templates, and non-type values that are
used to compose P participate in template argument deduction. That is,
they may be used to determine the value of a template argument, and
the value so determined must be consistent with the values determined
elsewhere. In certain contexts, however, the value does not
participate in type deduction, but instead uses the values of template
arguments that were either deduced elsewhere or explicitly specified.
If a template parameter is used only in non-deduced contexts and is
not explicitly specified, template argument deduction fails.
The non-deduced contexts are:
— The nested-name-specifier of a type that was specified using a qualified-id.
You can avoid this by few ways. First way - make operator < friend for class AbstractIteratorBase, or its member.
template<class TT>
class AbstractBaseIterator:
public std::iterator<std::random_access_iterator_tag,
typename std::iterator_traits<TT>::value_type>
{
public:
template<typename Iter>
friend bool operator < (const AbstractBaseIterator<Iter>& lhs, const AbstractBaseIterator<Iter>& rhs)
{
return lhs.base() < rhs.base();
}
protected:
TT _M_current;
const TT&
base() const
{ return this->_M_current; }
};
Second variant is define AbstractBaseIterator class not in template class. And then typedef AbstractBaseIterator<T> iterator; in AbstractBaseOrgan. If you can use C++11 you can use something like this.
class BaseClass
{
virtual ~BaseClass() {}
};
template<class TT>
class AbstractBaseIterator:
public std::iterator<std::random_access_iterator_tag,
typename std::iterator_traits<TT>::value_type>
{
protected:
TT _M_current;
const TT&
base() const
{ return this->_M_current; }
};
template<typename Iter>
bool operator < (const AbstractBaseIterator<Iter>& lhs, const AbstractBaseIterator<Iter>& rhs)
{
return lhs.base() < rhs.base();
}
template<class T>
class AbstractBaseOrgan: public BaseClass
{
public:
typedef T value;
template<typename TT>
using iterator = AbstractBaseIterator<TT>;
protected:
value te;
};
int main()
{
AbstractBaseOrgan<int>::iterator<int*> temp;
AbstractBaseOrgan<int>::iterator<int*> temp2;
int ttemp;
if(operator< (temp,temp2))
ttemp = 0;
return 0;
}
template<typename T>
struct A
{
A<T> operator%( const T& x);
};
template<typename T>
A<T> A<T>::operator%( const T& x ) { ... }
How can I use enable_if to make the following specialization happen for any floating point type (is_floating_point)?
template<>
A<float> A<float>::operator%( const float& x ) { ... }
EDIT:
Here's an answer I came up which is different from the ones posted below...
template<typename T>
struct A
{
T x;
A( const T& _x ) : x(_x) {}
template<typename Q>
typename std::enable_if<std::is_same<Q, T>::value && std::is_floating_point<Q>::value, A<T> >::type operator% ( const Q& right ) const
{
return A<T>(fmod(x, right));
}
template<typename Q>
typename std::enable_if<std::is_convertible<Q, T>::value && !std::is_floating_point<Q>::value, A<T> >::type operator% ( const Q& right ) const
{
return A<T>(x%right);
}
};
Like the below posters say, using enable_if may not be ideal for this problem (it's very difficult to read)
Use overloading instead of explicit specialization when you want to refine the behavior for a more specific parameter type. It's easier to use (less surprises) and more powerful
template<typename T>
struct A
{
A<T> operator%( const T& x) {
return opModIml(x, std::is_floating_point<T>());
}
A<T> opModImpl(T const& x, std::false_type) { /* ... */ }
A<T> opModImpl(T const& x, std::true_type) { /* ... */ }
};
An example that uses SFINAE (enable_if) as you seem to be curious
template<typename T>
struct A
{
A<T> operator%( const T& x) {
return opModIml(x);
}
template<typename U,
typename = typename
std::enable_if<!std::is_floating_point<U>::value>::type>
A<T> opModImpl(U const& x) { /* ... */ }
template<typename U,
typename = typename
std::enable_if<std::is_floating_point<U>::value>::type>
A<T> opModImpl(U const& x) { /* ... */ }
};
Way more ugly of course. There's no reason to use enable_if here, I think. It's overkill.
You can also use a default boolean template parameter like this:
template<typename T>
struct A
{
T x;
A( const T& _x ) : x(_x) {}
template<bool EnableBool = true>
typename std::enable_if<std::is_floating_point<T>::value && EnableBool, A<T> >::type
operator% ( const T& right ) const
{
return A<T>(fmod(x, right));
}
template<bool EnableBool = true>
typename std::enable_if<!std::is_floating_point<T>::value && EnableBool, A<T> >::type
operator% ( const T& right ) const
{
return A<T>(x%right);
}
};
With C++20
You can achieve that simply by adding requires to restrict the relevant template function:
template<typename Q> // the generic case, no restriction
A<T> operator% ( const Q& right ) const {
return A<T>(std::fmod(x, right));
}
template<typename Q> requires std::is_integral_v<T> && std::is_integral_v<Q>
A<T> operator% ( const Q& right ) const {
return A<T>(x % right);
}
The requires clause gets a constant expression that evaluates to true or false deciding thus whether to consider this method in the overload resolution, if the requires clause is true the method is preferred over another one that has no requires clause, as it is more specialized.
Code: https://godbolt.org/z/SkuvR9