2012-02-05 3 views
5

Я хотел бы скрыть std :: tuple в моем классе «Запись» и предоставить ему [[]] доступ к элементам кортежа. Наивный код, который не компилируется это:C++ Прямой вызов функции-члена без шаблона для функции шаблона

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

г ++ 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[] так же, как на массиве. Это возможно?

ответ

0

Поскольку параметр 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 
3

Аргумент get является константой времени компиляции. Вы не можете использовать переменную runtime для этого, и у вас не может быть одной функции, которая возвращает членов 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) или что мы могли бы создать что-то, что спрятало бы его достаточно хорошо, ваши пользователи не поймут, что их тип возврата просто изменился, а неявное преобразование делайте противные вещи. Предоставляя этим явным сейфам некоторые проблемы.

+0

@ Lol4t0 Последнее является хорошей отправной точкой. Почему вы хотите изменить аргумент шаблона MyT :: get в MyT :: get (size_t i)? Будут ли ваши пользователи действительно ожидать, что [i] будут иметь разные типы в зависимости от i? –

+0

@JohanLundberg Извините, я не понимаю. Это было направлено на меня? – pmr

+0

А, нет, я согласен с тобой. «ты» в моем последнем посте означала быть ОП. –

2

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

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

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

2

Я думаю, что у 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; 
} 
+0

Действительно приятно. Я попытался изменить ваш код, чтобы использовать 'std :: ref()' around 'get' и возвращаемые типы' decltype() & ', но пока я не справился. –

+0

@JohanLundberg Да, когда Xeo выходит онлайн, у него должен быть правильный код. По крайней мере, я думаю, что это Xeo. – Pubby

+0

Вау, работает ли этот код? Во время компиляции вы не можете использовать значения времени выполнения. – Xeo

2

Нет.

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

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

Record<Apple, Orange> fruitBasket; 

Тогда мы имеем:

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

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

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

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

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

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

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

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

0

Если вы хорошо с константой времени компиляции и по-прежнему хотят иметь хороший 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; 
} 

Live example on Ideone. Хотя я лично только советую сделать это:

// 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>(); 
} 

Live example on Ideone.

Смежные вопросы