I'm trying to create my own variant class that can be cast to and from many types. This leads to issues with ambiguous calls for operators like '+' and '-'. Is it possible to create a preference for which casts occur to avoid this?
#include <string>
class Variant
{
public:
Variant() {}
Variant(int i) : mData(std::to_string(i)) {}
Variant(double d) : mData(std::to_string(d)) {}
operator int() const { return lround(atof(mData.c_str())); }
operator double() const { return atof(mData.c_str()); }
private:
std::string mData;
};
int main() {
Variant v = 1.2;
int i = v; //fine
double d = v; //fine
double x = v - 1.0; //error, ambiguous, would be nice to prefer a double cast
}
You may do something like
#include <iostream>
#include <string>
#include <type_traits>
template <typename T>
struct S
{
template <typename T2>
T2 operator-(const T2& i_lhs) const
{
static_assert(std::is_convertible_v<T, T2>);
auto result = static_cast<T2>(m_member);
result -= i_lhs;
return result;
}
T m_member = 20;
};
And use it
int main()
{
auto s = S<int>();
auto s3 = s - 10.0;
std::cout << s3 << std::endl;
}
One more thing, so as to make the behaviour intuitive, provide a number of operators (i.e. Numeric + Variant, Variant + Numeric, and Variant + Variant).
And, the provided code returns a double. It may be more intuitive to return a new Variant.
Related
I'm started playing around with templates and came accross a problem with a need to call a function based on 2 template parameter types. I'm writting simple unit library that will have helper getConvertionFactor functions that will define ratio between units. I'd like to be able to pickup a function in template operators +,-,*,/ defintion based on types beeing pased as arguments. I know it's possible when these arguments are passed futher to getConvertionFactor function. Then overload resolution will pickup function that has specified ratio between given arguments type. But this approach as I undestand will result with unnecessary copying of LHS and RHS parameters into getConvertionFactor function just to perform overload resolution. I'd like to avoid it and perform somehow lookup of a function based on 2 types without need to pass dummy parameters into it.
#include <iostream>
struct Kilometer {
int val = 0;
};
struct NauticalMile {
int val = 0;
};
template<class FromType, class ToType>
double getConvertionFactor() {
return FromType::getBaseValue();
}
// How to define such conceptually function pointer lookup based on these 2 types?
// double getConvertionFactor<Kilometer, NauticalMile>() {
// return 0.1;
// }
// Works but requires copying parameters
double getConvertionFactor(Kilometer km, NauticalMile /*nm*/) {
return 0.1 * km.val;
}
template<class LHS_Unit, class RHS_Unit>
LHS_Unit operator+(const LHS_Unit& nm, const RHS_Unit& km) {
LHS_Unit lhs;
lhs.val = nm.val + getConvertionFactor(nm, km)*km.val;
return lhs;
}
int main() {
Kilometer km{100};
NauticalMile nm{100};
auto res = km + nm;
std::cout << res.val << std::endl;
return 0;
}
You can do it like this:
template<class FromType, class ToType>
double getConvertionFactor() {
return 1;
}
template<>
double getConvertionFactor<Kilometer, NauticalMile>() {
return 1.852;
}
template<>
double getConvertionFactor<NauticalMile, Kilometer>() {
return 1 / 1.852;
}
template<class LHS_Unit, class RHS_Unit>
LHS_Unit operator+(const LHS_Unit& lhs, const RHS_Unit& rhs) {
return { lhs.val + getConvertionFactor<LHS_Unit, RHS_Unit>() * rhs.val };
}
But it might be a good idea to implement user-defined conversion operators so that the types are implicitly convertible to one another.
#include <iostream>
struct Kilometer;
struct NauticalMile;
struct Kilometer {
double val = 0;
operator NauticalMile() const;
};
struct NauticalMile {
double val = 0;
operator Kilometer() const;
};
Kilometer::operator NauticalMile() const {
return { val / 1.852 };
}
NauticalMile::operator Kilometer() const {
return { val * 1.852 };
}
template<class LHS_Unit, class RHS_Unit>
LHS_Unit operator+(const LHS_Unit& lhs, const RHS_Unit& rhs) {
return { lhs.val + static_cast<LHS_Unit>(rhs).val };
}
int main() {
Kilometer km{ 100 };
NauticalMile nm{ 100 };
auto res = km + nm;
std::cout << res.val << std::endl;
return 0;
}
The following does not compile:
#include <iostream>
int main()
{
int a{},b{},c{},d{};
for (auto& s : {a, b, c, d}) {
s = 1;
}
std::cout << a << std::endl;
return 0;
}
Try it on godbolt
Compiler error is: error: assignment of read-only reference 's'
Now in my actual case the list is made of member variables on a class.
Now, this doesn't work because the expression becomes an initializer_list<int> that actually copies a,b,c, and d - hence also not allowing modification.
My question is two-fold:
Is there any motivation behind not allowing to write a range-based for loop in this way ? eg. perhaps there could be a special case for naked brace expressions.
What is a syntactical neat way of fixing this type of loop ?
Something along this line would be preferred:
for (auto& s : something(a, b, c, d)) {
s = 1;
}
I do not consider pointer indirection a good solution (that is {&a, &b, &c, &d}) - any solution should give the element reference directly when the iterator is de-referenced.
Ranges are not as magic as people would like. In the end, there must be an object that the compiler can generate calls on to either a member function or free function begin() and end().
Closest you'll probably be able to come is:
#include <iostream>
int main()
{
int a{},b{},c{},d{};
for (auto s : {&a, &b, &c, &d} ) {
*s = 1;
}
std::cout << a << "\n";
return 0;
}
Just another solution within a wrapper idea:
template<typename T, std::size_t size>
class Ref_array {
using Array = std::array<T*, size>;
class Iterator {
public:
explicit Iterator(typename Array::iterator it) : it_(it) {}
void operator++() { ++it_; }
bool operator!=(const Iterator& other) const { return it_ != other.it_; }
decltype(auto) operator*() const { return **it_; }
private:
typename Array::iterator it_;
};
public:
explicit Ref_array(Array args) : args_(args) {}
auto begin() { return Iterator(args_.begin()); }
auto end() { return Iterator(args_.end()); }
private:
Array args_;
};
template<typename T, typename... Ts>
auto something(T& first, Ts&... rest) {
static_assert((std::is_same_v<T, Ts> && ...));
return Ref_array<T, 1 + sizeof...(Ts)>({&first, &rest...});
}
Then:
int main() {
int a{}, b{}, c{}, d{};
for (auto& s : something(a, b, c, d)) {
std::cout << s;
s = 1;
}
std::cout << std::endl;
for (auto& s : something(a, b, c, d))
std::cout << s;
}
outputs
0000
1111
According to the standard §11.6.4 List-initialization/p5 [dcl.init.list] [Emphasis Mine]:
An object of type 'std::initializer_list' is constructed from an
initializer list as if the implementation generated and materialized
(7.4) a prvalue of type “array of N const E”, where N is the number of
elements in the initializer list. Each element of that array is
copy-initialized with the corresponding element of the initializer
list, and the std::initializer_list object is constructed to refer
to that array. [ Note: A constructor or conversion function selected
for the copy shall be accessible (Clause 14) in the context of the
initializer list. — end note ] If a narrowing conversion is required
to initialize any of the elements, the program is ill-formed.
Thus, your compiler is complaining legitimately (i.e., auto &s deducts to int const& s and you cannot assign to s in the ranged for loop).
You could alleviate this problem by introducing a container instead of an initializer list (e.g., `std::vector’) with ‘std::reference_wrapper’:
#include <iostream>
#include <vector>
#include <functional>
int main()
{
int a{},b{},c{},d{};
for (auto& s : std::vector<std::reference_wrapper<int>>{a, b, c, d}) {
s.get()= 1;
}
std::cout << a << std::endl;
return 0;
}
Live Demo
To satisfy that syntax
for (auto& s : something{a, b, c, d}) {
s = 1;
}
you might create wrapper:
template <typename T>
struct MyRefWrapper
{
public:
MyRefWrapper(T& p) : p(&p) {}
T& operator =(const T& value) const { return *p = value; }
operator T& () const { return *p; }
private:
T* p;
};
Demo
Solution: use a reference wrapper
template <class It>
struct range_view_iterator : public It{//TODO: don't inherit It
auto& operator*() {
return (*this)->get();
}
};
template<class It>
range_view_iterator(It) -> range_view_iterator<It>;
template<class T>
struct range_view {
std::vector<std::reference_wrapper<T> > refs_;
range_view(std::initializer_list<std::reference_wrapper<T> > refs) : refs_{refs} {
}
auto begin() {
return range_view_iterator{ refs_.begin() };
}
auto end() {
return range_view_iterator{ refs_.end() };
}
};
Then used as:
for (auto& e : range_view<int>{a, b, c, d}) {
e = 1;
}
This doesn't try to answer the first question though.
You could create wrapper class for storing reference and which will have assignment operator to update this value:
template<class T>
struct Wrapper {
T& ref;
Wrapper(T& ref)
: ref(ref){}
template<class U>
void operator=(U u) {
ref = u;
}
};
template<class...T>
auto sth(T&...t) {
return std::array< Wrapper<std::common_type_t<T...> > ,sizeof...(t) >{Wrapper(t)...};
};
int main(){
int a{},b{},c{},d{};
for (auto s : sth(a,b,c,d)) {
s = 1;
}
std::cout << a << std::endl; // 1
Live demo
The following code works:
struct A
{
int v = 3;
};
namespace Foo
{
template <int k=11>
int operator+(A const& lhs, A const& rhs)
{
return lhs.v + rhs.v + k;
}
}
using Foo::operator+;
int main()
{
A a1, a2;
std::cout << a1 + a2 << std::endl;
return 0;
}
The using Foo::operator+; directive brings Foo::operator+ into the external lookup scope and the when operator+ is used in the cout call, the default template value of 11 is taken and the result is, as expected: 17 (=3+3+11).
My question is how to change the using clause to explicitly instantiate the operator+ function with a non-default template value?
The line using Foo::operator+<42> does not work.
This is due to ISO C++ Standard 7.3.3.5: A using-declaration shall not name a template-id.
Is there a way around this?
Answering myself...
The problem seems to stem from ISO C++ Standard 7.3.3.5:
A using-declaration shall not name a template-id.
This prevents the acceptance of: using Foo::operator+<42>.
As a work-around I found the following solution that does what I need, at the cost of an extra namespace redirection. The code may still need some massaging but it does get the task done with minimal duplication on the user's side.
See a working version here.
struct A
{
int v = 0;
};
template <int k>
struct Bar
{
static int plus(A const& lhs, A const& rhs)
{
return rhs.v + lhs.v + k;
}
};
namespace Boo
{
using Baz = Bar<42>; // same as `typedef Bar<42> Baz;`
//#include "foo_operators.h"
namespace Foo
{
int operator+(A const& rhs, A const& lhs)
{
return Baz::plus(lhs, rhs);
}
}
}
namespace Goo
{
using Baz = Bar<3>;
//#include "foo_operators.h"
namespace Foo
{
int operator+(A const& rhs, A const& lhs)
{
return Baz::plus(lhs, rhs);
}
}
}
using namespace std;
int main()
{
{
using Boo::Foo::operator+;
A a1, a2;
cout << a1 + a2 << endl;
}
{
using Goo::Foo::operator+;
A a1, a2;
cout << a1 + a2 << endl;
}
return EXIT_SUCCESS;
}
// In real code extract to foo_operators.h: the partial file snippets to get #included multiple times
// namespace Foo
// {
// int operator+(A const& rhs, A const& lhs)
// {
// return Baz::plus(lhs, rhs);
// }
// }
The idea is to replace the Foo namespace with a struct template with static methods Bar.
This allows instantiating the Foo type using the desired params.
The operators just call the static methods via the externally defined and parametrized type.
ADL takes care of the rest.
In the example above, the user created 2 new namespaces, Boo and Goo to have 2 different parametrizations of the plus operator. Finally, at the point of usage the user brings in the desired version of the operator+ with the using directive.
In this approach there doesn't seem to be an option for specifying default parameter values.
In real code the operators themselves would be stored in a snippet file to be #includeed into the code after the declaration of the parameterized type (Baz in the example).
Take 2
Here's a much cleaner version that uses a simple templated traits class and avoid both the extra namespace and the operator redirection function plus.
template <int k>
struct op_traits_t
{
static const int K = k;
};
namespace Boo
{
using op_traits = op_traits_t<42>; // same as `typedef op_traits_t<42> op_traits;`
//#include "foo_operators.h"
// this is a partial file snippet
int operator+(A const& rhs, A const& lhs)
{
return rhs.v + lhs.v + op_traits::K;
}
}
namespace Goo
{
using op_traits = op_traits_t<3>;
//#include "foo_operators.h"
// this is a partial file snippet
int operator+(A const& rhs, A const& lhs)
{
return rhs.v + lhs.v + op_traits::K;
}
}
int main()
{
{
using Boo::operator+;
A a1, a2;
cout << a1 + a2 << endl;
}
{
using namespace Goo;
A a1, a2;
cout << a1 + a2 << endl;
}
return EXIT_SUCCESS;
}
std::cout << operator+<12>(a1, a2) << std::endl;
But don't do this. Operator + should behave in an unsurprising way.
Use a named function:
namespace Foo
{
template <int k=11>
int add_plus_k(A const& lhs, A const& rhs)
{
return lhs.v + rhs.v + k;
}
}
Let us consider following example:
#include <type_traits>
#if 1
struct X {};
struct O
{
O(X) { ; }
};
#else
struct O {};
struct X
{
operator O () { return {}; }
};
#endif
static_assert(std::is_convertible< X, O >::value);
struct S
{
void f(X) const { ; }
void f(O) { ; }
};
#include <cstdlib>
int
main()
{
S s;
s.f(X{});
return EXIT_SUCCESS;
}
Live example
It gives an error:
error: call to member function 'f' is ambiguous
When I removing const-qualifier, then the error cease to exist. Equally the same happens, if I add const-qualifier to the second overloading of f. I.e. if both overloadings are equally const-qualified, then all is OK.
Why is it so?
My compiler is clang 3.8.
Member functions have implicit parameter this.
So to call one of the functions f the compiler needs either to convert this to type const S * or to convert X to O.
Neither conversion regarding all parameters is better. So the compiler issues an error.
Why is it so?: The reason here is because the const-ness of the s object itself is also considered in overload resolution. Since s is non-const it would require adding const to the implicit this pointer to call the const f. Calling the non-const f is an exact match for the this pointer but requires an implicit conversion from X to O via O's converting constructor.
Mark B & Vlad from Moscow answer why, I just reply how can you call them.
you should add explicit to avoid compiler implicit conversion
#include <iostream>
struct X {};
struct O
{
explicit O(X) { ; }
O() = default;
O(O const &) = default;
O(O &&) = default;
};
struct S
{
void f(X) const { ; }
void f(O) { ; }
};
#include <cstdlib>
int main()
{
S s;
s.f(X{});
return EXIT_SUCCESS;
}
EDIT
if your type O is in 3party lib
you can add a template to do this.
#include <iostream>
using namespace std;
struct X {};
struct O {
O(X) {
;
}
O() = default;
O(O const&) = default;
O(O&&) = default;
};
struct S {
void f(const X) const {
cout << "X" << endl;
}
void f(O) {
cout << "O" << endl;
}
};
#include <cstdlib>
template<typename TA, typename TB>
void myCall(TA a, TB b) {
((const TA &&)a).f(b);
}
template<>
void myCall(S a, O b) {
a.f(b);
}
template<>
void myCall(S a, X b) {
((const S)a).f(b);
}
int main() {
S s;
myCall(s, X());
//s.f(X());
return EXIT_SUCCESS;
}
I need to allow the user to change members of two data structures of the same type at the same time. For example:
struct Foo { int a, b; }
Foo a1 = {1,2}, a2 = {3,4};
dual(a1,a2)->a = 5;
// Now a1 = {5,2} and a2 = {5,2}
I have a class that works and that change first a1 and then copy a1 into a2. This is fine as long as:
the class copied is small
the user doesn't mind about everything being copied, not only the part modified.
Is there a way to obtain this behavior:
dual(a1,a2)->a = 5;
// Now a1 = {5,2} and a2 = {5,4}
I am opened to alternative syntax, but they should stay simple, and I would like to avoid things like:
set_members(a1, a2, &Foo::a, 5);
members(a1, a2, &Foo::a) = 5;
or anything involving specifying explictely &Foo::
[Edit]
I should be more precise. The point is to work with a graph library. The library works on directed graph, but usage dictate that given two vertices, v1 and v2, if there is an edge v1->v2, then there will be an edge v2->v1. And these two edges have, very often (but not always) the same properties. So the current implementation now allows:
G.edge(v1,v2)->b = 5; // Only v1->v2 is modified
G.arc(v1,v2)->a = 10;
// Now G.edge(v2,v1) is set to G.edge(v1,v2) after the modification a = 10 (i.e. b = 5 too)
And I would like the notation to imply that only a is modified.
Relatively simple solution with Boost.Lambda:
#include <boost/lambda/lambda.hpp>
using namespace boost::lambda;
template<typename T, typename U, typename V>
void dual(const T& functor, U& a1, V& a2)
{
functor(a1);
functor(a2);
}
struct Foo
{
int a;
};
struct Bar
{
char a;
};
int main()
{
Foo a1;
Bar a2;
dual(_1 = 5, a1.a, a2.a);
}
Extending dual() with variadic templates / Boost.Preprocessor shenanigans is left as an exercise to the reader.
//to get the desired syntax
template<class T>
class SetPropertyProxy
{
public:
SetPropertyProxy(T& _v1, T& _v2)
: a(_v1, _v2) {}
class A_Property_Proxy
{
public:
A_Property_Proxy(T& _v1, T& _v2): v1(_v1), v2(_v2) {}
A_Property_Proxy& operator = (T::A_Property_Type val)
{
v1.a = val;
v2.a = val;
return *this;
}
private:
T& v1;
T& v2;
}
//public member "a"
A_Property_Proxy a;
};
//helper function
template<class T>
SetPropertyProxy<T> dual(T& a , T& b)
{ return SetPropertyProxy<T>(a,b); }
//usage
dual(a,b).a = 5; //calls A_Property_Proxy::operator =
It can be improved further making A_Property_Proxy class reusable by parameterizing by property type and taking references to properties instead of references to property containers (edges in this case)
template<class U>
class Property_Proxy
{
public:
Property_Proxy(U& _v1prop, U& _v2prop): v1prop(_v1prop), v2prop(_v2prop) {}
Property_Proxy& operator = (U val)
{
v1prop = val;
v2prop = val;
return *this;
}
private:
U& v1prop;
U& v2prop;
}
Edit (putting this here because comments don't have formatting)
So are you saying that your current code has lots of this:
G.edge(v3,v4)->a = 2;
G.edge(v3,v4)->b = 2;
G.edge(v4,v5)->a = 6;
G.edge(v4,v5)->b = 6;
And a very little bit of this:
G.edge(v5,v6)->a = 4;
G.edge(v5,v6)->b = 7;
And your goals are [1] make it easier to spot those special cases [2] less verbose code?
----- original answer, may be irrelevant now -----
Here's a general idea, there are lots of possible improvements:
class MagicBag
{
private:
// you could make the whole class a template
// instead of hard-coding Foo..
vector<Foo *> m_vec;
public:
// store references to the items
void Add(Foo *f) { m_vec->push_back(f); }
// you can do overloads instead of these setters...
void set_a(int val) {
for (vector<Foo>::iterator i = m_vec.start(); i != m_vec.end(); ++i)
(*i)->a = val;
}
void set_b(int val) {
for (vector<Foo>::iterator i = m_vec.start(); i != m_vec.end(); ++i)
(*i)->b = val;
}
}
Usage:
Foo a1 = {1,2}, a2 = {3,4};
MagicBag mb;
mb.Add(&a1);
mb.Add(&a2);
mb.set_a(5); // now a1.a = 5 and a2.a = 5
// etc.
This is easier semantically in languages which support Properties, such as C#. There the final syntax would be:
mb.a = 5;
By introducing an abuse of templates I can get most of the desired syntax. This compiles and works, but no guarantees are made about it. It requires adding some macros the struct to be used and requires use of set_* instead of direct assignment.
#include <iostream>
#define PROPERTY_MAP(ClassName) \
struct hidden_Mapper { \
ClassName * m_d1; \
ClassName * m_d2; \
hidden_Mapper(Data * d1, Data * d2) : \
m_d1(d1), m_d2(d2) {}
#define DECLARE_PROPERTY(name)\
template <typename ValueType> \
void set_##name(const ValueType & value) \
{ m_d1->name = value; m_d2->name = value; } \
#define END_PROPERTY_MAP };
template<typename ClassType>
typename ClassType::hidden_Mapper dual(ClassType & d1, ClassType & d2)
{
return typename ClassType::hidden_Mapper(&d1, &d2);
}
struct Data
{
int a;
float b;
PROPERTY_MAP(Data)
DECLARE_PROPERTY(a)
DECLARE_PROPERTY(b);
END_PROPERTY_MAP
};
int main()
{
Data d1, d2;
dual(d1, d2).set_a(5);
dual(d1, d2).set_b(5.7);
std::cout << d1.a << d2.a << d1.b << d2.b <<std::endl;
}
struct proxy {
struct column {
column(T &a, T &b);
column& operator=(T);
T &a, &b;
};
proxy(U &A, U &B);
column operator[](int i) { return column(A[i], B[i]; }
U &A, &B;
};
proxy(A, B)[0] = 5;
// or you could be evil, overload ",", and get this syntax
(A, B)[0] = 5;
or some sort of variation