2016-04-08 4 views
2

Пожалуйста, рассмотрим следующий фрагмент кода:Чрезмерная оператора плюс для набора классов

template<class E> 
class vector_expression 
{}; 

template<class Tuple> 
class vector 
    : public vector_expression<vector<Tuple>> 
{}; 

template<class E1, class E2, class BinaryOperation> 
class vector_binary_operation 
    : public vector_expression<vector_binary_operation<E1, E2, BinaryOperation>> 
{ 
public: 
    vector_binary_operation(E1&& e1, E2&& e2, BinaryOperation op) 
     : m_e1(e1), m_e2(e2), 
      m_op(op) 
    {} 

private: 
    E1 m_e1; 
    E2 m_e2; 
    BinaryOperation m_op; 
}; 

template<class E1, class E2> 
vector_binary_operation<E1, E2, std::plus<>> operator+(E1&& e1, E2&& e2) { 
    return{ std::forward<E1>(e1), std::forward<E2>(e2), std::plus<>{} }; 
} 

Код выше гарантирует, что vector_binary_operation хранит ссылки на именованные объекты и делает копии для временных. Проблема заключается в интерфейсе operator+, потому что он фактически определяет этот оператор для любого типа. Что мне нужно изменить, если я хочу сохранить функциональность, но только определить оператор для типов, которые являются или производными от vector_expression?

+1

список инициализаторов в конструкторе должен быть лучше: 'm_e1 (станд :: вперед (e1)), m_e2 (станд :: вперед (е2)), m_op (Std :: ход (оп)) '. В противном случае вы нарушите цепочку безупречной пересылки. – davidhigh

+0

@ davidhigh Да, вы правы. Я скопировал код из другого вопроса и там параметры 'e1' и' e2' были ссылками на const. Спасибо, что заметили это. – 0xbadf00d

+0

нет, я ошибаюсь. Я был бы прав, если конструктор был шаблоном функции, что-то вроде 'template vector_binary_operation (E1 _ && e1, E2 _ && e2, BinaryOperation op)'. Кстати, это, вероятно, то, что вы намеревались, когда вы использовали '&&'. Для получения дополнительной информации см. [Здесь] (https://isocpp.org/blog/2012/11/universal-references-in-c11-scott-meyers) – davidhigh

ответ

2

Вы можете использовать SFINAE и std::is_base_of следующим образом:

template<class E1, class E2> 
std::enable_if_t<std::is_base_of_v<vector_expression<std::decay_t<E1>>, std::decay_t<E1>>&& 
       std::is_base_of_v<vector_expression<std::decay_t<E2>>, std::decay_t<E2>>, 
       vector_binary_operation<std::decay_t<E1>, std::decay_t<E2>, std::plus<>>> 
operator+(E1&& e1, E2&& e2) { 
    return { 
      std::forward<std::decay_t<E1>>(e1), 
      std::forward<std::decay_t<E2>>(e2), 
      std::plus<>{} 
      }; 
} 

Live Demo

+2

Потребности 'распада'. –

+0

@ T.C спасибо, и им нужно много их :). – 101010

+0

, и ему нужно много помощников '_t', чтобы скрыть' typename T :: type' – TemplateRex

1

@ 101010 уже дал принцип. Я немного расширяю его, задавая этот вопрос и в комментарии одного из моих answers.

Как я предполагаю, что вы перегрузки больше операторов, чем operator+, это удобно, чтобы написать класс черт, который проверяет, все ли прошло типы vector_expression s:

template<typename ... Ts> struct is_vector_expression 
    : public std::false_type {}; 
template<typename T> struct is_vector_expression<T> 
    : public std::is_base_of<vector_expression<std::decay_t<T> >, std::decay_t<T> >::type {}; 
template<typename T, typename ... Ts> struct is_vector_expression<T, Ts ...> 
    : public std::integral_constant<bool, is_vector_expression<T>::value 
             && is_vector_expression<Ts ...>::value> {}; 

При использовании C++ 17, вы можете пропустить материал вариационного шаблона и использование std::conjunction.

Далее вы можете обернуть, что внутри подходящего псевдонима, так что вам не придется писать std::enable_if_t все время:

template<typename ... Ts> 
using enable_for_vector_expression = std::enable_if_t<is_vector_expression<Ts...>::value>; 

Наконец, вы можете использовать его как в следующем примере для всех ваших перегружен операторы:

template<typename E1, typename E2, 
     typename = enable_for_vector_expression<E1, E2> > 
auto operator+(E1&& e1, E2&& e2) 
{ 
    //... 
} 
+0

Как бы вы реализовали свой признак 'is_vector_expression' с' std :: union'? – 0xbadf00d

+0

Может 'шаблон <имяТипа ... Ts> \t \t с использованием enable_if_vector_expression_t = зЬй :: enable_if_t <станд :: conjunction_v <станд :: is_base_of_v > зЬй :: decay_t > ... >>; '? – 0xbadf00d

+0

@ 0xbadf00d: Я бы применил его на уровне 'is_vector_expression' (и оставить псевдоним таким, как есть), но в противном случае - точно так, как вы написали. – davidhigh