我有这个代码:
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("%sn", e->s.c_str());
BaseType tmp = e->*groupPtr;
printf("%sn", tmp.c_str());
}
};
int main(){
Test t;
t.s = "x";
Base<Test, &Test::s> r;
r.Process(&t);
}
然而,编译以一个错误结束:
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
我使用的是启用了C++17的Visual Studio 2019。
为什么结构不能自动推导?或者这可能吗?
似乎c++忘记在TMP中包含成员指针的自动推导。我尝试过使用c++20,但失败了。这是一个大问题。但我们可以有一个解决方法,如下所示。
struct Test {
std::string s;
};
template <typename T,typename BaseType,
BaseType (T::*groupPtr)>
struct Base{
void Process(const Test * e){
printf("%sn", e->s.c_str());
BaseType tmp = e->*groupPtr;
printf("%sn", 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);
}
上面的编码终于起作用了。
Clang正确地认为此代码是有效的:auto
可以用作函数、变量或模板参数的任何类型声明的decl说明符。另一方面,你不能在声明中的其他地方使用它:
int auto::*f() {…} // not in a ptr-operator
std::vector<auto> x=…; // not in a template argument
您的模板需要更多的信息才能执行您想要的操作。如果不是不可能的话,也很难在不使用"可疑"(应该避免使用reintertret_cast<>(指针gymnatics的情况下做你想做的事情。使用宏会给我们带来一些语法上的好处。
此示例使用引用以便于使用,修改以使用(或添加suuport for(指针应该足够容易。
#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{});
}