Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ Forward non-template member function call to template function

I'd like to hide a std::tuple in my class 'Record' and provide an operator[] on it to access elements of the tuple. The naive code that does not compile is this:

#include <tuple>

template <typename... Fields>
class Record {
  private:
    std::tuple<Fields...> list;

  public:
    Record() {}

    auto operator[](std::size_t n)
            -> decltype(std::get<1u>(list)) {
        return std::get<n>(list);
    }
};

int main() {
    Record<int, double> r;
    r[0];
    return 0;
}

g++ 4.6 says:

x.cc:13:32: error: no matching function for call to ‘get(std::tuple<int, double>&)’
x.cc:13:32: note: candidates are:
/usr/include/c++/4.6/utility:133:5: note: template<unsigned int _Int, class _Tp1, class _Tp2> typename std::tuple_element<_Int, std::pair<_Tp1, _Tp2> >::type& std::get(std::pair<_Tp1, _Tp2>&)
/usr/include/c++/4.6/utility:138:5: note: template<unsigned int _Int, class _Tp1, class _Tp2> const typename std::tuple_element<_Int, std::pair<_Tp1, _Tp2> >::type& std::get(const std::pair<_Tp1, _Tp2>&)
/usr/include/c++/4.6/tuple:531:5: note: template<unsigned int __i, class ... _Elements> typename std::__add_ref<typename std::tuple_element<__i, std::tuple<_Elements ...> >::type>::type std::get(std::tuple<_Elements ...>&)
/usr/include/c++/4.6/tuple:538:5: note: template<unsigned int __i, class ... _Elements> typename std::__add_c_ref<typename std::tuple_element<__i, std::tuple<_Elements ...> >::type>::type std::get(const std::tuple<_Elements ...>&)

Basically I'd like to call Record::operator[] just like on an array. is this possible?

like image 829
user1190714 Avatar asked Feb 05 '12 14:02

user1190714


2 Answers

The argument to get is a compile time constant. You cannot use a runtime variable for this and you cannot have a single function that returns the tuple members as your return type is going to be wrong. What you can do is to abuse non-type argument deduction:

#include <tuple>

template<typename... Args>
struct Foo {
  std::tuple<Args...> t;

  template<typename T, std::size_t i>
  auto operator[](T (&)[i]) -> decltype(std::get<i>(t)) {
    return std::get<i>(t);
  }
  // also a const version
};

int main()
{
  Foo<int, double> f;
  int b[1];
  f[b];
  return 0;
}

This is so horrible, that I would never use it and it won't make much sense to users. I would just forward get through a template member.

I'll try to explain why I think why this is really evil: The return type of a function depends only on compile time facts (this changes slightly for virtual member functions). Let's just assume that non-type argument deduction were possible for some cases (the function call arguments are constexpr) or that we could build something that hides it reasonably well, your users wouldn't realize that their return type just changed and implicit conversion would do nasty things to them. Making this explicit safes some of the trouble.

like image 57
pmr Avatar answered Oct 26 '22 23:10

pmr


The error message seems to be misleading, as the problem with your code is pretty much clear:

 auto operator[](std::size_t n)
            -> decltype(std::get<1u>(list)) {
        return std::get<n>(list);
    }

The template argument n to std::get must be a constant expression, but in your code above n is not a constant expression.

like image 23
Nawaz Avatar answered Oct 26 '22 23:10

Nawaz