C ++ Переадресация вызова функции-члена без шаблона в функцию шаблона - PullRequest
5 голосов
/ 05 февраля 2012

Я хотел бы спрятать std :: tuple в своем классе 'Record' и предоставить оператор [] для доступа к элементам кортежа.Наивный код, который не компилируется, таков:

#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 говорит:

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 ...>&)

В основном я хотел бы вызвать Record::operator[], как в массиве.это возможно?

Ответы [ 6 ]

3 голосов
/ 05 февраля 2012

Аргументом get является постоянная времени компиляции.Вы не можете использовать переменную времени выполнения для этого, и у вас не может быть единственной функции, которая возвращает члены tuple, так как ваш тип возврата будет неправильным.Что вы можете сделать, так это злоупотребить выводом не типового аргумента:

#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;
}

Это так ужасно, что я бы никогда не использовал его, и это не будет иметь большого смысла для пользователей.Я просто перешлю get через член шаблона.

Я попытаюсь объяснить, почему я думаю, почему это действительно зло: тип возвращаемого значения функции зависит только от фактов времени компиляции (это немного меняется дляvirtual функций-членов).Давайте просто предположим, что в некоторых случаях возможен вывод аргументов, не относящихся к типу (аргументы вызова функции constexpr), или что мы могли бы создать что-то, что скрывает это, ваши пользователи не поймут, что их возвращаемый тип просто изменился и неявныйобращение сделало бы с ними неприятные вещи.Делая это явным, избавляет от некоторых неприятностей.

2 голосов
/ 05 февраля 2012

Нет .

Невозможно использовать параметр, связанный во время выполнения (например, параметр функции), чтобы действовать в качестве параметра шаблона, потому что такие должны быть связаны во время компиляции.

Но давайте на секунду представим, что это было:

Record<Apple, Orange> fruitBasket;

Тогда мы бы получили:

  • decltype(fruitBasket[0]) равно Apple
  • decltype(fruitBasket[1]) равно Orange

разве здесь что-то вас не беспокоит?

В C ++ сигнатура функции определяется типами его аргументов (и, необязательно, значений параметров шаблона).Тип возврата не учитывается и не участвует (в лучшую или в худшую сторону) в разрешении перегрузки.

Поэтому функция, которую вы пытаетесь создать, просто не имеет смысла.

Теперь,у вас есть две альтернативы:

  • требуют, чтобы все аргументы наследовали или могли быть преобразованы в общий тип, и возвращали этот тип (что позволяет предлагать не шаблонную функцию)
  • embraceшаблонов и требуют, чтобы ваши пользователи специально указывали индекс типа, который они хотят использовать

Я не знаю (и не могу), какой вариант предпочтительнее в вашей конкретной ситуации, у вас будет выбор дизайнасделать.

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

2 голосов
/ 05 февраля 2012

Я думаю, что у Xeo был код, который сделал это.

Вот моя попытка, которая несколько работает. Проблема в том, что [] не является ссылкой.

template<typename T, std::size_t N = std::tuple_size<T>::value - 1>
struct foo {
  static inline auto bar(std::size_t n, const T& list)
          -> decltype(((n != N) ? foo<T, N-1>::bar(n, list) : std::get<N>(list))) {
      return ((n != N) ? foo<T, N-1>::bar(n, list) : std::get<N>(list));
  }
};

template<typename T>
struct foo<T, 0> {
  static inline auto bar(std::size_t n, const T& list)
          -> decltype(std::get<0>(list)) {
      return std::get<0>(list);
  }
};

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

  public:
    Record() {
      std::get<0>(list) = 5;
    }

    inline auto operator[](std::size_t n) 
            -> decltype(foo<decltype(list)>::bar(n, list)) {
            return foo<decltype(list)>::bar(n, list);
    }
};

int main() {
    Record<int, double> r;
    std::cout << r[0];
    return 0;
}
2 голосов
/ 05 февраля 2012

Это сообщение об ошибке вводит в заблуждение, поскольку проблема с вашим кодом в значительной степени ясна:

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

Аргумент шаблона n до std::get должен быть константным выражением, но в вашемкод выше n является не константным выражением.

0 голосов
/ 05 февраля 2012

Если вы хорошо справляетесь с константой времени компиляции и все еще хотите иметь красивый синтаксис operator[], это интересный обходной путь:

#include <tuple>

template<unsigned I>
struct static_index{
  static unsigned const value = I;
};

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

  public:
    Record() {}

    template<unsigned I>
    auto operator[](static_index<I>)
        -> typename std::tuple_element<
               I, tuple_t>::type&
    {
        return std::get<I>(list);
    }
};

namespace idx{
const static_index<0> _0 = {};
const static_index<1> _1 = {};
const static_index<2> _2 = {};
const static_index<3> _3 = {};
const static_index<4> _4 = {};
}

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

Живой пример на Ideone.1006 * Хотя лично я бы посоветовал сделать это:

// member template
template<unsigned I>
auto get()
    -> typename std::tuple_element<
           I, tuple_t>::type&
{
    return std::get<I>(list);
}

// free function
template<unsigned I, class... Fields>
auto get(Record<Fields...>& r)
  -> decltype(r.template get<I>())
{
  return r.template get<I>();
}

Живой пример на Ideone.

0 голосов
/ 05 февраля 2012

Поскольку n является параметром шаблона, он должен быть известен во время компиляции, но вы хотите передать его как параметр во время выполнения.

Кроме того, gcc 4.5.2 не счастлив из-за этого факта:

g++ 1.cpp -std=c++0x
1.cpp: In member function 'decltype (get<1u>(((Record<Fields>*)0)->Record<Fields>::list)) Record<Fields>::operator[](size_t)':
1.cpp:14:25: error: 'n' cannot appear in a constant-expression
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...