C++自动推导模板成员指针的类型



我有这个代码:

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,但失败了。这是一个大问题。但我们可以有一个解决方法,如下所示。

  • 考虑您的代码应该可以工作,但由于"c++限制"而无法工作。我们只会对其进行一点修改。请按如下操作。
    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{});
    }
    

    最新更新