Reputation: 9527
I have this code:
struct Test {
std::string s;
};
template <typename T,
auto T::* groupPtr>
struct Base{
using BaseType = typename std::decay<decltype(std::declval<T>().*groupPtr)>::type;
void Process(const Test * e){
printf("%s\n", e->s.c_str());
BaseType tmp = e->*groupPtr;
printf("%s\n", tmp.c_str());
}
};
int main(){
Test t;
t.s = "x";
Base<Test, &Test::s> r;
r.Process(&t);
}
However, compilation ends with an error:
main.cpp: error C2440: 'specialization': cannot convert from
'std::string Test::* ' to 'auto Test::* '
main.cpp: message : Types pointed to are unrelated; conversion
requires reinterpret_cast, C-style cast or function-style cast
main.cpp: error C3535: cannot deduce type for 'auto Test::* ' from
'int'
main.cpp: message : see reference to class template instantiation
'Base<Test,0>' being compiled
I am using Visual Studio 2019 with C++17 enabled.
Why is construction cannot be auto-deduced? Or is it even possible?
Upvotes: 4
Views: 843
Reputation: 497
It seems c++ forgot to include auto deduction for member pointers in TMP. I have tried with c++20 and failed. This is a big issue. But we can have a workaround for the same as in the following.
struct Test {
std::string s;
};
template <typename T,typename BaseType,
BaseType (T::*groupPtr)>
struct Base{
void Process(const Test * e){
printf("%s\n", e->s.c_str());
BaseType tmp = e->*groupPtr;
printf("%s\n", tmp.c_str());
}
};
int main(){
Test t;
t.s = "x";
static constexpr auto (Test::*s)=&Test::s;
Base<Test,std::decay<decltype(std::declval<Test>().*s)>::type, s> r;
r.Process(&t);
}
The above coding finally works.
Upvotes: 2
Reputation: 39818
Clang is correct that this code is valid: auto
can be used as a decl-specifier for any kind of declaration of a function, variable, or template parameter. You can’t, on the other hand, use it in other places in the declaration:
int auto::*f() {…} // not in a ptr-operator
std::vector<auto> x=…; // not in a template argument
Upvotes: 2
Reputation: 6471
Your template needs more info than that to do what you want. It is also very difficult, if not impossible to do what you want without using 'questionable' (one should avoid using reinterptret_cast<>) pointer gymnatics. And using macros will give us some syntactic sugar.
This example uses references for ease of use, modifying to use (or add suuport for) pointers should be easy enough.
#include <cstddef> // offsetof()
#include <iostream> // std::cout
#include <string>
template <typename _ClassT, typename _MemberT, size_t _Offset>
struct Base {
void Process(const _ClassT& e) {
std::cout << GetMemberRef(e); // your code used printf(), but you shouldn't
// assume the type of the inner member.
// That's the whole point of this exercise,
// isn't it?
auto& tmp = GetMemberRef(e);
std::cout << tmp;
}
// the name of access functions is quite verbose, but that's for
// demonstration purposes only.
static const _MemberT& GetMemberRef(const _ClassT& e) {
return *reinterpret_cast<const _MemberT*>(
reinterpret_cast<const char*>(&e) + _Offset);
}
static _MemberT& GetMemberRef(_ClassT& e) {
return *reinterpret_cast<_MemberT*>(reinterpret_cast<char*>(&e) + _Offset);
}
};
// utility to make instantiation a bit easier to read and write...
// I don't think there is a way to do that without a macro.
#define MakeBase(type, member) \
Base<type, decltype(type::member), offsetof(type, member)> {}
// test the code...
struct Test {
std::string s;
};
struct Test2 {
int value = 42;
};
int main() {
Test t;
t.s = "x";
// declaration is a bit awkward, but there are no ways around that.
Base<Test, decltype(Test::s), offsetof(Test, s)> r;
r.Process(t);
// using MakeBase utility macro is quite clean, though.
auto x = MakeBase(Test2, value);
x.Process(Test2{});
}
Upvotes: -3