Por que o compilador não está selecionando minha sobrecarga de templos de função no exemplo a seguir?
-
21-09-2019 - |
Pergunta
Dados os seguintes modelos de função:
#include <vector>
#include <utility>
struct Base { };
struct Derived : Base { };
// #1
template <typename T1, typename T2>
void f(const T1& a, const T2& b)
{
};
// #2
template <typename T1, typename T2>
void f(const std::vector<std::pair<T1, T2> >& v, Base* p)
{
};
Por que o código a seguir sempre chama a sobrecarga nº 1 em vez de sobrecarga nº 2?
int main()
{
std::vector<std::pair<int, int> > v;
Derived derived;
f(100, 200); // clearly calls overload #1
f(v, &derived); // always calls overload #1
return 0;
}
Dado que o segundo parâmetro de f
é um tipo derivado de Base
, Eu esperava que o compilador escolhesse a sobrecarga nº 2, pois é uma correspondência melhor do que o tipo genérico na sobrecarga nº 1.
Existem técnicas que eu poderia usar para reescrever essas funções para que o usuário possa escrever o código como exibido no main
função (ou seja, alavancando o compilador dedicação dos tipos de argumentos)?
Solução
Você pode fazer isso:
f(v, static_cast<Base*>(&derived));
Ou use Sfinae para remover a primeira função como candidato a seleção:
// Install boost library and add these headers:
#include <boost/utility/enable_if.hpp>
#include <boost/type_traits.hpp>
// #1 - change it to look like this (note the keyword void changed positions)
template <typename T1, typename T2>
typename boost::disable_if<
typename boost::is_convertible<T2, Base*>, void>::type
f(const T1& a, const T2& b)
{
};
// #2 - this one can stay the same
template <typename T1, typename T2>
void f(const std::vector<std::pair<T1, T2> >& v, Base* p)
{
};
Outras dicas
Dado que o segundo parâmetro de F é um tipo de base derivado
É conversível a tal, mas é um derivado*. A primeira função de modelo não requer conversões e a segunda exige uma, portanto, escolhe o primeiro.
Isso escolhe o segundo:
f(v, static_cast<Base*>(&derived));
Em uma nota lateral, main
retorna um int
.
Além dos tópicos óbvios sobre Lookup Koenig Isso é mais ou menos bem implementado pelos compiladores (especialmente os mais velhos são bastante problemáticos), existem algumas armadilhas em relação Especialização de modelos.
A especialização exige que os tipos correspondam exatamente (não tenho certeza de como a DST define isso, mas da minha experiência [GCC, MSVC], uma classe derivada não será correspondente). Se você adicionar um elenco feio à base*, ele deve funcionar como você pretende, opcionalmente adicionar outra especialização para derivados ...