The question is simple, how you use implicit construction/conversion for templated class in C++14.
Comment on the code below,
A is what is expected, automatic construction/conversion of const char* to A.
B is a templated class, I do not have the expected behavior of A and to get a "similar" behavior I need to specify the type against which I'll, in this case test compare equal (note the explicit const T* and not const B<T>&). This is problematic because I would expect an automatic conversion from const T* to B<T> as specified in B's constructor.
C is a templated class and exposes the behavior of A. It is a better solution to the problem than B is. The problem, it requires the use of a template trick, see below. The goal would be to ditch this trick. The trick solves the problem, can we make it work without it ?
Note: the code below is compiling on "recent" versions of g++/clang++/msvc and icc without error.
Code below accessible here: https://godbolt.org/z/W9x5Kcqrn
#include <cassert>
#include <iostream>
struct A {
A(const char *aze) { std::cout << "A ctor\n"; }
};
template <typename T>
struct B {
B(const T *aze) { std::cout << "B<T> ctor\n"; }
};
template <typename T>
struct C {
C(const T *aze) { std::cout << "C<T> ctor\n"; }
};
////////////////////////////////
bool operator==(const A &, const A &) {
std::cout << "A==A\n";
return true;
}
////
template <typename T>
bool operator==(const B<T> &, const T *) { // I dont want const T* here, I already have a constructor that can do it for me
std::cout << "B<T>==T*\n";
return true;
}
template <typename T>
bool operator==(const T *, const B<T> &) { // I dont want const T* here, I already have a constructor that can do it for me
std::cout << "T*==B<T>\n";
return true;
}
////
// I would like to ditch this TypeIdentity trickery
template <class T>
struct TypeIdentity {
using type = T;
};
template <class T>
using TypeIdentity_t = typename TypeIdentity<T>::type;
template <typename T>
bool operator==(const C<T> &, const TypeIdentity_t<C<T>> &) {
std::cout << "C<T>==C<tT>\n";
return true;
}
template <typename T>
bool operator==(const TypeIdentity_t<C<T>> &, const C<T> &) {
std::cout << "C<tT>==C<T>\n";
return true;
}
template <typename T>
bool operator==(const C<T> &, const C<T> &) {
std::cout << "C<T>==C<T>\n";
return true;
}
////////////////////////////////
int main() {
// Works as expected, conversion of "xrb" to A and then operator==(const A &,
// const A &) called
// Prints: A ctor A ctor A==A
assert(A{"aze"} == "xrb");
// Works as expected, operator==(const B<T> &, const T *) called
// Prints: B<T> ctor B<T>==T*
assert(B<char>{"aze"} == "xrb");
// Works as expected, operator==(const T *, const B<T> &) called
// Prints: B<T> ctor T*==B<T>
assert("aze" == B<char>{"xrb"});
// Work as expected with TypeIdentity_t, does not compile without it
assert(C<char>{"aze"} == "xrb");
assert("xrb" == C<char>{"aze"});
assert(C<const char>{"aze"} == "xrb");
// Works as expected, operator==(const C<T> &, const C<T> &) called
// Prints: C<T> ctor C<T> ctor C<T>==C<T>
assert(C<char>{"aze"} == C<char>{"xrb"});
}
Looking forward to your answers, Thanks