I'm trying to write a C++ function template that, when called with an argument of a classes member alias, the function can deduce the class of that alias. A simplified version of what would be nice:
class A {
public :
using x_t = float;
void whatami() {printf("A\n");}
};
class B {
public :
using x_t = int;
void whatami() {printf("B\n");}
};
template<typename T>
void fn(T::x_t x) {
T t;
t.whatami();
};
...
A::x_t a;
fn(a);
...
The g++ 7.3 error is:
play$ g++ --std=c++11 x.cpp
x.cpp:19:12: error: variable or field ‘fn’ declared void
void fn(T::x_t x) {
^~~
x.cpp:19:16: error: expected ‘)’ before ‘x’
void fn(T::x_t x) {
^
x.cpp: In function ‘int main(int, char**)’:
x.cpp:29:3: error: ‘fn’ was not declared in this scope
fn(a);
^~
I'm interpreting this to mean that C++ can't deduce T as in fn(T::x_t x)
but I'm not sure.
Thoughts?
It can't deduce T
from float
, which is what a
is. It wont treat typedefs with any special consideration when trying to fill your template. It's also not going to search the whole type space for any type with a member by that name.
One way to get that deduction is to boost that member type with a reference to the owning class, and add an implicit conversion back to the primitive:
template <typename TOwner, typename TData>
class x_t {
TData data;
public:
constexpr x_t() = default;
constexpr x_t(TData data) : data(data) { }
constexpr operator TData() const { return data; }
};
struct A {
using x_t = x_t<A, float>;
/*...*/
};
Then partial specialization will work correctly:
template<typename TOwner, typename TData>
void fn(x_t<TOwner, TData> x) {
TOwner t;
t.whatami();
};
And you can use an instance in any type-safe function
A::x_t a(42.0);
std::cout << a; // 42.0
(sadly, printf
doesn't count as type-safe. using streams here, fmt::printf
from the excellent fmt library would also work).
It probably doesn't reach your goal of making it easy to deduce from a member, but it's a possibility.