2015-05-04 3 views
3

Я пытаюсь написать класс сравнения шаблонов std::sort, который должен получить неизвестное количество кортежей (вариационный шаблон). Каждый кортеж должен состоять из столбца (некоторый тип, который у нас есть в нашем коде) и bool, указывающий, следует ли сортировать этот столбец в порядке возрастания или убывания.std :: tuple как аргумент шаблона?

В принципе, я хочу что-то похожее на это:

// doesn't compile - conceptual code 
template <typename std::tuple<Col, bool>> 
struct Comparator 
{ 
    bool operator() (int lhs, int rhs) 
    { 
     // lhs and rhs are row indices. Depending on the columns 
     // and the bools received, decide which index should come first 
    } 
} 

Является такого рода вещи можно в C++ 11?

+0

@BaummitAugen Поскольку позже я хочу добавить специализацию шаблона (должен был упомянуть об этом) на основе типов столбцов – Shmoopy

+0

Не следует ли 'operator()' принимать 'tuple <>'? Это можно использовать только для сортировки целых чисел ... – Barry

+0

@Barry сортирует целые числа, но по отношению к столбцам, переданным в качестве аргументов шаблона (целые числа представляют индексы строк, а столбцы представляют, какие столбцы сортировать) – Shmoopy

ответ

4

Да, это возможно, - вы хотите частичную специализацию Comparator:

template <typename T> 
struct Comparator; 

template <typename Col> 
struct Comparator<std::tuple<Col, bool>> 
{ 
    // ... 
}; 
0

Возможно ли это? Да, но для этого вам нужны довольно уродливые шаблонные трюки.

//a trait for checking if a type is of the form std::tuple<T,bool> 
template <class Tuple> 
struct is_col_bool_tuple : false_type{}; 

template <typename Col> 
struct is_col_bool_tuple<std::tuple<Col,bool>> : true_type{}; 

//a helper struct for checking if all the values in a boolean pack are true 
template<bool...> struct bool_pack; 
template<bool... bs> 
using all_true = std::is_same<bool_pack<bs..., true>, bool_pack<true, bs...>>; 

//a trait to check if a list of types are all of the form std::tuple<T,bool> 
template <class... Tuples> 
using are_col_bool_tuples = all_true<is_col_bool_tuple<Tuples>::value...>; 

//an incomplete type for when we pass incorrect template arguments 
//this impl helper is needed because variadic parameters need to be last 
template <typename Enable, class... Tuples> 
struct ComparatorImpl; 

//our specialized implementation for when the template arguments are correct 
template <class... Tuples> 
struct ComparatorImpl<std::enable_if_t<are_col_bool_tuples<Tuples...>::value>, 
         Tuples...> 
{ 
    bool operator() (int lhs, int rhs) 
    { 
     //do your comparison 
    } 
}; 

//a nice alias template for forwarding our types to the SFINAE-checked class template 
template <class... Tuples> 
using Comparator = ComparatorImpl<void, Tuples...>; 
+0

Может ли downvoter объяснить, что в этом плохого? Может, я неправильно понял вопрос? – TartanLlama

+0

Я тоже не понимаю вопроса (я не сделал ни слова) – Barry

0

Как правило, да, вы можете частично специализировать вариационные шаблоны.

В вашем случае я бы использовал эти 3 специализации:

Во-первых, общее определение, как вы можете видеть, что вам не нужно, чтобы определить его тело:

template <typename ...ColOrderType> 
class Comparator; 

Во-вторых, промежуточный шаг:

template <typename Col, typename ...ColOrderType> 
struct Comparator<std::tuple<Col,bool>, ColOrderType...> 
    : public Comparator<ColOrderType...> 
{ 
    typedef Comparator<ColOrderType...> Base; 
    Comparator(std::tuple<Col,bool> const& firstOrder, 
       ColOrderType&&... nextOrders) 
     : Base(std::forward<ColOrderType>(nextOrders)...), 
     currectOrder(firstOrder) 
    {} 
    // this is implementation of `operator <` - feel free to change 
    template <typename T> 
    bool operator() (T const& lhs, T const& rhs) 
    { 
     auto&& lcol = get(std::get<0>(currentOrder), lhs); 
     auto&& rcol = get(std::get<0>(currentOrder), rhs); 
     bool order = std::get<1>(currentOrder); 
     if (lcol < rcol) return order; 
     if (rcol < lcol) return not order; 
     return Base::operator()(lhs, rhs); 
    } 
    std::tuple<Col,bool> currentOrder; 
}; 

А класс сторожевого:

template <> 
struct Comparator<> 
{ 
    template <typename T> 
    bool operator() (T const& lhs, T const& rhs) 
    { 
     return false; 
    } 
}; 

Обратите внимание, что вы не сможете передавать типы, отличные от std :: tuple из двух элементов - определение Comparator не позволяет это - поэтому общее определение не определено.

И о get функции в этой части:

 auto&& lcol = get(std::get<0>(currentOrder), lhs); 
     auto&& rcol = get(std::get<0>(currentOrder), rhs); 

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