Как неявно преобразовать объект шаблона класса при передаче в качестве аргумента в шаблон функции? - PullRequest
0 голосов
/ 21 ноября 2018

Учитывая следующий небольшой фрагмент кода:

template <typename T>
class A{
public:
    A() { };
    ~A() { };
    // ...
    template <typename outT = T> operator std::vector<outT>(){ /* implicit/explicit cast to std::vector */ }    
};

template <typename T = float>
void myFunc(const std::vector<T>& _arg){
    printf("myFunc\n");
}

int main(int argc, char const *argv[]) { 
    A<int> foo;
    myFunc(foo);
}

При попытке компиляции я получаю ошибку

template argument deduction/substitution failed: [...]  ‘A<int>’ is not derived from ‘const std::vector<T>’

Хотя, с другой стороны, если myFunc нетшаблон, он будет компилироваться и запускаться просто отлично.Я предполагаю, что ошибка вызвана тем фактом, что, поскольку myFunc принимает несколько векторных типов в качестве аргумента, компилятор не знает, к какому типу он должен преобразовать foo.Однако не должны ли значения шаблона по умолчанию использоваться в качестве запасного варианта в таком случае?Есть ли альтернативный способ передачи объекта класса A в myFunc?

Ответы [ 3 ]

0 голосов
/ 21 ноября 2018

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

См. Вывод аргумента шаблона по ссылке на C ++.

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

См. « Шаблоны функций C ++: как они действительно работают? » на YouTube.

0 голосов
/ 22 ноября 2018

Как отмечено в других ответах, разрешение шаблона не может учитывать неявные преобразования.Простой способ решить проблему - перегрузить myFunc и выполнить явное приведение.Рассмотрим следующий код:

#include <vector>
#include <iostream>

template <typename T>
class A{
public:
    A() { };
    ~A() { };
    // ...
  template <typename outT = T> operator std::vector<outT>() const { /* implicit/explicit cast to std::vector */
    std::cout << "I am doing a conversion" << std::endl;
    return std::vector<outT>(); }
};

template <typename T = float, typename... Args>
void myFunc(const std::vector<T, Args...>& _arg){
  std::cout << "myFunc" << std::endl;
}

template <typename T>
void myFunc(const A<T>& _arg)
{
  std::cout << "I am the second variant of myFunc" << std::endl;
  myFunc(static_cast<const std::vector<T>>(_arg));
}

int main(int argc, char const *argv[]) { 
    A<int> foo;
    std::cout << "-- First call of myFunc" << std::endl;
    myFunc(foo);
    std::cout << "-- Second call of myFunc" << std::endl;
    std::vector<double> x = foo;
    myFunc(x);

    return 0;
}

Вывод программы:

-- First call of myFunc
I am the second variant of myFunc
I am doing a conversion
myFunc
-- Second call of myFunc
I am doing a conversion
myFunc

Когда вы передаете foo в myFunc, компилятор использует вторую перегрузку myFunc, котораячерез явное приведение вызывает оператор преобразования.Во втором случае мы непосредственно присваиваем foo std::vector, следовательно, выполняем преобразование перед передачей результирующего std::vector первой перегрузке myFunc.Обратите внимание:

  • вам необходим оператор преобразования const, если вы хотите сохранить постоянство при перегрузках myFunc.

  • васнужен дополнительный пакет параметров шаблона typename... Args при первой перегрузке myFunc для захвата каждой специализации std::vector (скажем, с пользовательским распределителем).

Другой вариант может бытьобъявить A как класс, производный от std::vector.Что-то вроде следующего кода:

#include <vector>
#include <iostream>

template <typename T>
class A : public std::vector<T> {
public:
  A() : std::vector<T>(/* Some parameters to initialize the base class */) { }
};

template <typename T = float, typename... Args>
void myFunc(const std::vector<T, Args...>& _arg){
  std::cout << "myFunc" << std::endl;
}

int main(int argc, char const *argv[]) { 
    A<int> foo;
    myFunc(foo);

    return 0;
}

В этом случае вам не нужно преобразование, и вы вызываете myFunc путем нарезки.Недостатком этого подхода является то, что вы выполняете преобразование в std::vector в конструкторе, и каждый раз, когда вы изменяете A, вы должны поддерживать данные базового класса std::vector в актуальном состоянии.

0 голосов
/ 21 ноября 2018

Неявные преобразования не учитываются при попытке вывести параметры шаблона.Поскольку A<int> не может соответствовать const std::vector<T>, шаблон myFunc не является подходящим кандидатом.Это остается верным, даже если вы явно создаете экземпляр заранее (и даже если оператор преобразования не является шаблонным ).

Для разрешения перегрузки неявное преобразование рассмотрено, поэтому версия без шаблонов работает.Шаблонная версия никогда не будет участвовать, потому что она не является подходящим кандидатом.

...