Given a template and a more specialized overload:
template <typename T>
const T& some_func(const T& a, const T& b)
{
std::cout << "Called base template\n";
return (a < b) ? a : b;
}
template <typename T>
T* const& some_func(T* const& a, T* const& b)
{
std::cout << "Called T* overload\n";
return (*a < *b) ? a : b;
}
Then the following works as expected:
int main()
{
std::cout << some_func(5.3, 6.2) << "\n";
double a = 1;
double b = 2;
double *p = &a;
double *q = &b;
const double *ret = some_func(p, q);
std::cout << *ret << "\n";
return 0;
}
With the first printing Called base template, the second printing Called T* overload. If we replace the overload signature with:
template <typename T>
const T*& some_func(const T*& a, const T*& b)
then the second call now calls the base template. Given that int const& x is equivalent to const int& x, am I incorrect in assuming T* const& is equivalent to const T*&? Why is the first version resolved properly while the second is not?
Yes you are incorrect. In const T*& T is const, in T* const& the pointer (T*) is const.
The change happens when you shift const to the right of *. const T*& and T const *& are equivalent but T* const& is different. Typedefs can help, given
typedef T *TPtr;
const TPtr& is equivalent to TPtr const & is equivalent to T* const &.
Declarators in C/C++ are hard to parse (for a human). Even the inventor of C said so.
T const & and const T & are identical; and U const * and const U * are identical.
But S & and T const & are not the same even for T = S. What you have is even worse, namely S = U const * and T = U *.
Related
What are the requirements to ensure that type equivalence can be used in the find function of a sorted container? Specifically how does mixed type comparison work.
Setup
Say I've got a struct, comparator, and set
struct Wrapper{int val;};
template<class T> struct compare {...};
template<class T> using wset=set<T,compare<T>>;
and a main function
int main() {
wset<Wrapper> s;
s.insert(Wrapper{1}); //direct insertion
s.insert(Wrapper{10});
s.insert(11); //insertion by implicit construction
s.insert(100);
for (int x : s)
cout << x << ' ';
auto it= s.find(10);//find by equivalence
cout<<*it;
return 0;
}
I can get the code to run by adding constructors and conversion overloads to the wrapper struct, and also adding parameterized binary predicates to the compare struct. But I don't understand how its actually working.
Equivalence Implementation
From my understanding find(a) will be implemented by searching the set for an object b that satisfies !(a<b)&&!(b<a). therefore multi type equivalence should only require defining Ta<Tb and Tb<Ta ie
bool operator()(Ta a, Tb b){...}
bool operator()(Tb b, Ta a){...}
but in practice the compiler fails to make that comparison if that's all it has to go on.
Possible Options
Its seems like my only options are to
let the wrapper object be constructed by the incoming type
struct Wrapper{
Wrapper(int val):val{val}{};
int val;
};
template<class T>
struct compare {
bool operator() (T const & a, T const & b) const {return a.val < b.val;}
};
which seems like it has an unnecessary object being constructed
to add cast operators to wrapper and the operators to compare mixed types
struct Wrapper
{
int val;
operator int()const {return val;};
};
template<class T>
struct compare {
using is_transparent=true_type;
bool operator() (T const & a, T const & b) const {return a.val < b.val;}
bool operator() (T const & a, int const & b) const {return a.val < b;}
bool operator() (int const & a, T const & b) const {return a < b.val;}
};
which may allow unintentional implicit conversion in other places.
to add cast operators to wrapper, and an operator to compare one type at time.
struct Wrapper{
int val;
operator int()const {return val;};
};
template<class T>
struct compare {
using is_transparent=true_type;
bool operator() (T const & a, T const & b) const {return a.val < b.val;}
bool operator() (int const & a, int const & b) const {return a < b;}
};
which like 2 may allow unintentional implicit conversion in other places.
Working examples
using a constructor, implicit conversion, and a less than operator to handle multiple types on insert and in find
#include <iostream>
#include <set>
using namespace std;
struct Wrapper{
Wrapper(int val):val{val}{};
int val;
operator int()const {return val;};
};
template<class T>
struct compare {
bool operator() (T const & a, T const & b) const {return a.val < b.val;}
};
template<class T>
using wset=set<T,compare<T>>;
int main() {
wset<Wrapper> s;
s.insert(Wrapper{1});
s.insert(Wrapper{10});
s.insert(11);
s.insert(100);
for (int x : s)
cout << x << ' ';
auto it= s.find(10);
cout<<*it;
return 0;
}
this works but it feels like its probably constructing a wrapper in order to do the find
using implicit conversion, parameterizing the comparator operators, and making the comparator transparent prevents unintentional construction
#include <iostream>
#include <set>
using namespace std;
struct Wrapper{
int val;
operator int()const {return val;};
};
template<class T>
struct compare {
using is_transparent=true_type;
bool operator() (T const & a, T const & b) const {return a.val < b.val;}
bool operator() (T const & a, int const & b) const {return a.val < b;}
bool operator() (int const & a, T const & b) const {return a < b.val;}
};
template<class T>
using wset=set<T,compare<T>>;
int main() {
wset<Wrapper> s;
s.insert(Wrapper{1});
s.insert(Wrapper{10});
s.insert(Wrapper{11});
s.insert(Wrapper{100});
for (int x : s)
cout << x << ' ';
auto it= s.find(10);
cout<<*it;
return 0;
}
but requires that for every type combination there is a converter from WT to T and a operator for both (WT,T) and (T,WT). The converter also can't be made explicit so it exposes potential accidental conversion
defining conversion between types and handle only one type per operator
#include <iostream>
#include <set>
using namespace std;
struct Wrapper{
int val;
operator int()const {return val;};
};
template<class T>
struct compare {
using is_transparent=true_type;
bool operator() (T const & a, T const & b) const {return a.val < b.val;}
bool operator() (int const & a, int const & b) const {return a < b;}
};
template<class T>
using wset=set<T,compare<T>>;
int main() {
wset<Wrapper> s;
s.insert(Wrapper{1});
s.insert(Wrapper{10});
s.insert(Wrapper{11});
s.insert(Wrapper{100});
for (int x : s)
cout << x << ' ';
auto it= s.find(10);
cout<<*it;
return 0;
}
which is simpler than option 2 but still relies on implicit conversion.
I'm looking to reverse the .* operator.
When I have a pointer I can dereference it with * and then from dereferenced value go back to the pointer by & operator.
With pointer-to-member I can dereference it with .* operator (supplying the object instance) but there is no operator to obtain the original pointer-to-member from object and its dereferenced field.
Consider the following data structure:
struct Point { double x, y, z; };
Now I need to get a pointer to member double Point::* from (Point&, double&) pair, where that double & is a field of the Point& object.
In other words I need a function to_member_ptr such that:
template<typename DataType, typename Member>
constexpr Member DataType::* obtain_member_ptr(const DataType &that, const Member &fieldInThat);
int main(int, char*[]) {
Point pt;
static_assert(obtain_member_ptr(pt, pt.x) == &Point::x, "error");
static_assert(obtain_member_ptr(pt, pt.y) == &Point::y, "error");
static_assert(obtain_member_ptr(pt, pt.z) == &Point::z, "error");
}
I could write it by hand for given datatype as below:
constexpr double Point::* obtain_member_ptr(const Point &that, const double &fieldInThat) {
if(&that.x == &fieldInThat) return &Point::x;
if(&that.y == &fieldInThat) return &Point::y;
if(&that.z == &fieldInThat) return &Point::z;
return nullptr;
}
But it seems like a plain boilerplate and I feel that there should be a way to make compiler do that for me.
How can I portably obtain a pointer to object from object and its field?
Using visit_struct, you may first add reflection:
struct Point { double x, y, z; };
VISITABLE_STRUCT(Point, x, y, z);
Then visit your struct:
template <typename C, typename T>
struct MemberPtrGetter
{
constexpr MemberPtrGetter(const C& c, const T& field) : c(c), field(field) {}
// Correct type, check reference.
constexpr void operator() (const char* name, T C::*member) const
{
if (&(c.*member) == &field)
{
res = member;
}
}
// other field type -> ignore
template <typename U> constexpr void operator() (const char* , U C::*member) const {}
const C& c;
const Member& field;
Member C::* res = nullptr;
};
template<typename C, typename T>
constexpr T C::* obtain_member_ptr(const C& c, const T& field)
{
MemberPtrGetter<C, T> visitor{c, field};
visit_struct::apply_visitor<C>(visitor);
return visitor.res;
}
I have this program with 2 template functions :
#include <iostream>
template <class T> void assign(T& t1,T& t2){
std::cout << "First method";
t1=t2;
}
template <class T> void assign(T& t1,const T& t2) {
std::cout << "Second method";
t1=t2;
}
class A
{
public:
A(int a):_a(a){};
private:
int _a;
friend A operator+(const A& l, const A& r);
};
A operator+(const A& l, const A& r) {
return A(l._a+r._a);
}
int main ()
{
A a=1;
const A b=2;
assign(a,a+b);
}
i cant understand why does the assign(a,a+b) call the second template function
, in the operator+ we are createing a new A object and call the ctor with an int parameter.
it is creating a+b as const object ?
it is creating a+b as const object ?
No, it is creating a temporary. Temporaries are binded to rvalue references. You can verify that with a 'third' function (a universal reference, in this case)
template <class T>
void assign(T& t1, T&& t2) {
std::cout << "Third method";
t1=t2;
}
As you don't have one, the compiler will pick the const reference overload. Why ?
Suppose you have
void add(int & x)
{
++x;
}
unsigned y = 0;
add(y); // create a temporary to int
std::cout << y << "\n"; // what's the value of y ?
I have the following code with overloaded template functions
#include <iostream>
using namespace std;
template <class T>
const T& max(const T& a1, const T& a2)
{
cout << "general template" << endl;
return (a1 < a2) ? a2 : a1;
}
template <class T>
const T* max(const T* a1, const T* a2)
{
cout << "max for pointers" << endl;
return (a1 < a2) ? a2 : a1;
}
template <class T>
const T& max(const T& a1, const T& a2, const T& a3)
{
cout << "general template with three parameters" << endl;
return ::max(::max(a1, a2), ::max(a1, a2));
}
int main()
{
int* a = new int(5);
int* b = new int(56);
int* c = new int(2);
int*const &g = ::max(a, b, c);
cout << *g << endl;
return 0;
}
I was expecting it to fail, because the max template with three parameters would return reference to a temporary variable (returned by template for pointers). But it works and calls general template function.
The question is why doesn't called template for pointers?
Thanks.
If you comment out the definition of 2-argument max for references, you would see that the code does not compile. The error that MSVC++2013 gives at line 22 is:
error C2440: 'return' : cannot convert from 'const int *' to 'int *const &'
That seems to be the reason why max for references is always selected: template substitution fails for max for pointers.
If you change the code to the following, then template for pointers is called:
#include <iostream>
using namespace std;
template <class T>
T max(const T& a1, const T& a2)
{
cout << "general template" << endl;
return (a1 < a2) ? a2 : a1;
}
template <class T>
T* max(T* a1, T* a2)
{
cout << "template for pointers" << endl;
return (a1 < a2) ? a2 : a1;
}
template <class T>
T max(const T& a1, const T& a2, const T& a3)
{
cout << "general template with three parameters" << endl;
return ::max(::max(a1, a2), a3);
}
int main()
{
int* a = new int(5);
int* b = new int(56);
int* c = new int(2);
int* g = ::max(a, b, c);
cout << *g << endl;
return 0;
}
If you comment out the definition of the template for pointers, then the template for references is called. The preference of template for pointers over template for references seems to happen because the type is already a pointer.
Here is some explanation on the order of template matching: What are the rules for choosing from overloaded template functions?
EDIT: the OP's code can be altered in another way, so that MSVC++2013 prefers reference-based template to pointer-based:
#include <iostream>
using namespace std;
template <class T>
T max(const T& a1, const T& a2)
{
cout << "general template" << endl;
return (a1 < a2) ? a2 : a1;
}
template <class T>
const T* max(const T* a1, const T* a2)
{
cout << "template for pointers" << endl;
return (a1 < a2) ? a2 : a1;
}
template <class T>
T max(const T& a1, const T& a2, const T& a3)
{
cout << "general template with three parameters" << endl;
return const_cast<const T>(::max(::max(a1, a2), a3));
}
int main()
{
int* a = new int(5);
int* b = new int(56);
int* c = new int(2);
int* g = ::max(a, b, c);
cout << *g << endl;
return 0;
}
This happens because in this version pointer-based template definition has additional qualifiers for its parameter types: they are not just T*, but rather const T*.
It's not working. It seems to be working, because you're only using the first two parameters or your three parameter max function.
template <class T>
const T& max(const T& a1, const T& a2, const T& a3)
{
cout << "general template with three parameters" << endl;
return ::max(
::max(a1, a2),
::max(a1, a2)); // HERE
}
Correcting that shows what is going on: You're comparing pointer addresses.
See here in action.
Your pointer overload isn't called because the reference version is a better fit: a1 etc ARE const references (to pointers, but well). So the reference version is a perfect match on respect to overload resolution.
I guess to achieve what you want you'd need some SFINAE magic here.
One thing I'd like to add: since comparing pointers is a frequent operation in C++, IMHO it would be very misleading to have some max dereference the pointers before comparing.
When you call max(a, b, c),T in max(const T& a1, const T& a2, const T& a3) becomes an aliases of int *,so in max(const T& a1, const T& a2, const T& a3)
max(a,b) will match max(const T& a1, const T& a2)
typedef int * T;
const T x;
const int * y; //they are different
I have the following C++ code:
#include <iostream>
template <class T>
void assign(T& t1, T& t2) {
std::cout << "First method" << std::endl;
t1 = t2;
}
template <class T>
void assign(T& t1, const T& t2) {
std::cout << "Second method" << std::endl;
t1 = t2;
}
class A {
public:
A(int a) : _a(a) {};
private:
int _a;
friend A operator+(const A& l, const A& r);
};
A operator+(const A& l, const A& r) {
return A(l._a + r._a);
}
int main() {
A a = 1;
const A b = 2;
assign(a, a);
assign(a, b);
assign(a, a+b);
}
The output is:
First method
Second method
Second method
The output stays the same even if I comment out the the first 2 assigns in the main function.
Can someone please explain to me why the operator+ returns a const A?
The output is the same in both Linux Debian 64bit and Windows 7 64 bit.
It doesn't return a const A at all. It returns a temporary A, which may only bind to a const reference.