C++ 自动推断模板成员指针的类型
C++ auto deduce type of template member pointer
我有这个代码:
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);
}
然而,编译以错误结束:
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。
为什么构造不能自动推导?或者有可能吗?
您的模板需要的信息比您想要的更多。如果不使用 'questionable'(应避免使用 reinterptret_cast<>)指针体操,即使不是不可能,也非常困难。使用宏会给我们一些语法糖。
此示例使用引用以方便使用,修改为使用(或添加 suuport)指针应该很容易。
#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{});
}
C++ 似乎忘了在 TMP 中包含成员指针的自动推导。我试过使用 c++20 但失败了。这是一个大问题。但是我们可以有一个与以下相同的解决方法。
考虑到您的代码应该可以工作,但由于 'c++ limitations' 而不能工作。我们只会修改它的一部分。如下操作。
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);
}
上面的代码终于可以工作了。
Clang 认为此代码有效是正确的:auto
可用作 decl-specifier 用于任何类型的函数、变量或模板声明范围。另一方面,你不能在声明的其他地方使用它:
int auto::*f() {…} // not in a ptr-operator
std::vector<auto> x=…; // not in a template argument
我有这个代码:
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);
}
然而,编译以错误结束:
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。
为什么构造不能自动推导?或者有可能吗?
您的模板需要的信息比您想要的更多。如果不使用 'questionable'(应避免使用 reinterptret_cast<>)指针体操,即使不是不可能,也非常困难。使用宏会给我们一些语法糖。
此示例使用引用以方便使用,修改为使用(或添加 suuport)指针应该很容易。
#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{});
}
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("%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);
}
上面的代码终于可以工作了。
Clang 认为此代码有效是正确的:auto
可用作 decl-specifier 用于任何类型的函数、变量或模板声明范围。另一方面,你不能在声明的其他地方使用它:
int auto::*f() {…} // not in a ptr-operator
std::vector<auto> x=…; // not in a template argument