У меня есть такой код:

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

Я использую Visual Studio 2019 с включенным C ++ 17.

Почему конструкция не может быть выведена автоматически? Или это вообще возможно?

4
Martin Perry 20 Ноя 2020 в 18:19

2 ответа

Вашему шаблону нужно больше информации, чтобы делать то, что вы хотите. Также очень сложно, если не невозможно, делать то, что вы хотите, без использования «сомнительной» (следует избегать использования reinterptret_cast <>) указательной гимнастики. А использование макросов придаст нам синтаксического сахара.

В этом примере для простоты использования используются ссылки, изменение использования (или добавление поддержки) указателей должно быть достаточно простым.

#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{});
}
-3
Michaël Roy 20 Ноя 2020 в 18:28

Clang правильно, что этот код действителен: auto может использоваться как спецификатор объявления для любого вида объявления функции, переменной или параметра шаблона. С другой стороны, вы не можете использовать его в других местах объявления:

int auto::*f() {…}      // not in a ptr-operator
std::vector<auto> x=…;  // not in a template argument
2
Davis Herring 21 Ноя 2020 в 05:34