2

In the code I attached I am looking for a way to map 1 type to another. The code fails to compile with the error

[root@]# g++ -std=c++11 test.cpp -o test; ./test
test.cpp: In member function ‘void A::Func(AType)’:
test.cpp:32:58: error: template argument 1 is invalid
     using BType = std::result_of<GetBTypeFromAType(AType)>::type;

The compiler version I'm using is 5.3.1, any idea how to fix that?

#include <iostream>
#include <typeinfo>
#include <cxxabi.h>
#include <memory>

template <class T>
std::string GetTypeName()
{
  //http://stackoverflow.com/questions/23266391/find-out-the-type-of-auto/23266701#23266701
  std::unique_ptr<char, void(*)(void*)> name{abi::__cxa_demangle(typeid(T).name(), 0, 0, nullptr), std::free};
  return name.get();
}


struct A
{
  struct AA {};
  struct AB {};
  struct AC {};

  struct BA {};
  struct BB {};
  struct BC {};

  BA GetBTypeFromAType(AA a) { return BA(); }
  BB GetBTypeFromAType(AB a) { return BB(); }
  BC GetBTypeFromAType(AC a) { return BC(); }

  template <typename AType>
  void Func(AType a)
  {
    using BType = std::result_of<GetBTypeFromAType(AType)>::type;
    std::cout << "Func called with " << GetTypeName<AType>() << " and got " << GetTypeName<BType>() << std::endl;
  }
};

int main()
{
  A a;
  A::AA aa;
  A::AB ab;
  A::AC ac;
  a.Func(aa);
  a.Func(ab);
  a.Func(ac);
  return 0;
}
e271p314
  • 3,841
  • 7
  • 36
  • 61
  • 3
    (Irrelevant, but don't do your dev work as root. Only elevate privs when you really have to.) – Mat Jan 10 '18 at 12:39

4 Answers4

5

The tricky thing about std::result_of is that the F(Args...) syntax is misleading. Since it looks like a function, you're tempted to call it like a function. (I think this wonky syntax is part of the reason for its deprecation in C++17)

However, F must be a type not a function name, so saying GetBTypeFromAType is insufficient (you need its type).

However, since it's overloaded, you can't simply wrap it in a decltype; you'd need to cast to the appropriate overload to get a correct pointer, which defeats the entire purpose (because if you knew which overload it would call you wouldn't need to be asking for the return type in the first place)

std::result_of is best used with a function object, so the easiest workaround is to wrap the call to GetBTypeFromAType in a lambda and then use decltype on that lambda:

template <typename AType>
void Func(AType a)
{
  auto wrapper = [this](AType&& a){return GetBTypeFromAType(std::forward<decltype(a)>(a));};
  using BType = typename std::result_of<decltype(wrapper)(AType)>::type;
  std::cout << "Func called with " << GetTypeName<AType>() << " and got " << GetTypeName<BType>() << std::endl;
}

Live Demo (C++11)

The other solutions presented here either assume we can default or aggregate initialize an AType or require that you write a type traits specialization which duplicates information already available at compile time.

AndyG
  • 39,700
  • 8
  • 109
  • 143
1

result_of is not what you want. result_of expects a callable, which you are not passing it. You could simplify it by using decltype directly, i.e:

template <typename AType>
void Func(AType a) {
  using BType = decltype(GetBTypeFromAType(AType{}));
  std::cout << "Func called with " << GetTypeName<AType>() << " and got " << GetTypeName<BType>() << std::endl;
}

This outputs:

Func called with A::AA and got A::BA
Func called with A::AB and got A::BB
Func called with A::AC and got A::BC
user167921
  • 330
  • 1
  • 7
  • I will use your code, this much easier than result_of but the question was specifically about result_of so I accepted @AndyG answer – e271p314 Jan 10 '18 at 13:13
1

Why don't you just create a simple trait class (provided that you actually don't need the original methods, but really only want mapping of one type to another):

template<typename T>
struct GetBTypeFromAType;

struct A
{
  struct AA {};
  struct AB {};
  struct AC {};

  struct BA {};
  struct BB {};
  struct BC {};

  template <typename AType>
  void Func(AType a)
  {
    using BType = typename GetBTypeFromAType<AType>::type;
    // ...
  }
};

template<> struct GetBTypeFromAType<A::AA> { using type = A::BA; };
template<> struct GetBTypeFromAType<A::AB> { using type = A::BB; };
template<> struct GetBTypeFromAType<A::AC> { using type = A::BC; };
Jaa-c
  • 5,017
  • 4
  • 34
  • 64
  • For some reason there is some problem with template specialization in my code, using overloaded functions works great for me. Nevertheless, your solution is probably the best for mapping one type to another – e271p314 Jan 10 '18 at 13:22
0

I think you are supposed to give the result type, not the function name, to std::result_of: F(Args...) is a function type with Args... being the argument types and F being the return type..

Maybe can you try to use this instead:

using BType = decltype(GetBTypeFromAType(a));

Example.

piwi
  • 5,136
  • 2
  • 24
  • 48