Как я могу заставить шаблонного оператора выводить правильный тип возвращаемого значения? - PullRequest
2 голосов
/ 12 апреля 2019

Скажем, мне нужны два ортогональных типа A и B, чтобы я мог написать

A a = b1 * b2; // b1,b2 of type B
B b = a1 * a2; // a1,a2 of type A

Данные, которыми они обмениваются, одинаковы, поэтому я попытался создать дизайн на основе политики. Некоторый код :

#include <type_traits>

struct isA {};
struct isB {};

template<typename T>
struct myClass
{
    int _data;

    template<typename U>
    myClass<U> operator * ( const myClass<T>& other );
};

template<typename T>
template<typename U>
myClass<U> myClass<T>::operator * ( const myClass<T>& other )
{
    // just an idea, will not be needed if correct instanciation
    static_assert( std::is_same<U,T>::value, "cannot return same type" );
   // ... here, some code
}


int main()
{
    myClass<isA> a1,a2;
    myClass<isB> b = a1 * a2;
}

Это не с:

main.cpp: In function 'int main()':
main.cpp:26:25: error: no match for 'operator*' (operand types are
'myClass<isA>' and 'myClass<isA>')
    myClass<isB> b = a1 * a2;
main.cpp:12:16: note: candidate: 'template<class U> myClass<U> myClass<T>::operator*(const myClass<T>&) [with U = U; T = isA]'
  myClass<U> operator * ( const myClass<T>& other );
 main.cpp:12:16: note:   template argument deduction/substitution failed:
 main.cpp:26:27: note:   couldn't deduce template parameter 'U'

Что я понимаю, это то, что он терпит неудачу, потому что это только аргументы функции, которые используются компиляторомдля генерации экземпляра, а не типа возврата.Таким образом, компилятор не может сгенерировать правильную реализацию для оператора.

Мой вопрос (довольно простой): как я могу реализовать этот оператор?

Здесь не требуется специализация шаблона, поведение - этото же самое с двумя типами (но другие функции - не показанные здесь - будут иметь конкретную реализацию для каждого из типов).Но я хочу подтвердить тот факт, что вы не можете это сделать: A a = a1 * a2;

Примечание: не удалось найти ни одного вопроса по этой теме, если вы его найдете, пожалуйста, ссылку!

Ответы [ 3 ]

3 голосов
/ 12 апреля 2019

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

namespace detail
{
    template<typename Out, typename In>
    MyClass<Out> times( const MyClass<In> & lhs, const MyClass<In> & rhs)
    {
        // shared code here
    }
}

myClass<isA> operator * ( const myClass<isB>& lhs, const myClass<isB>& rhs )
{ return detail::times<isA>(lhs, rhs); }

myClass<isB> operator * ( const myClass<isA>& lhs, const myClass<isA>& rhs )
{ return detail::times<isB>(lhs, rhs); }
2 голосов
/ 12 апреля 2019

Вы можете создать черту, которая отображает isA на isB и isB на isA.

namespace detail
{
    template<typename>
    struct myClassTraits;

    template<>
    struct myClassTraits<isA>
    {
        using other_type = isB;
    };

    template<>
    struct myClassTraits<isB>
    {
        using other_type = isA;
    };
}

template<typename T>
struct myClass
{
    int _data;

    using times_t = myClass<typename detail::myClassTraits<T>::other_type>;

    times_t operator * ( const myClass& other );
};
1 голос
/ 12 апреля 2019

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

Однако сделать

A a = b1 * b2; // b1,b2 of type B

работая, вы можете реализовать конструктор неявного преобразования, так что сначала вы получите тип B в результате оператора умножения, а затем он будет приведен к типу A:

template <typename U>
myClass(const myClass<U>& other)  {} // copy conversion constructor
template <typename U>
myClass(myClass<U>&& other)  {} // move conversion constructor

так что

A a = b1 * b2;

будет эквивалентно

A a = A(b1 * b2);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...