Como posso evitar que o C ++ adivinhe um segundo argumento de modelo?


26

Estou usando uma biblioteca C ++ ( strf ) que, em algum lugar dentro dela, tem o seguinte código:

namespace strf {
template <typename ForwardIt>
inline auto range(ForwardIt begin, ForwardIt end) { /* ... */ }

template <typename Range, typename CharT>
inline auto range(const Range& range, const CharT* sep) { /* ... */ }
}

Agora, quero usar strf::range<const char*>(some_char_ptr, some_char_ptr + some_length)no meu código. Mas, se o fizer, obtenho o seguinte erro (com o NVCC da CUDA 10.1):

error: more than one instance of overloaded function "strf::range" matches the argument list:
            function template "auto strf::range(ForwardIt, ForwardIt)"
            function template "auto strf::range(const Range &, const CharT *)"
            argument types are: (util::constexpr_string::const_iterator, util::constexpr_string::const_iterator)

O código da biblioteca provavelmente pode ser alterado para evitar isso (por exemplo, usando:

inline auto range(const typename std::enable_if<not std::is_pointer<typename std::remove_cv<Range>::type>::value, Range &>::type range, const CharT* sep)

garantir que Rangenão seja um ponteiro); mas não posso fazer essa alteração agora. Em vez disso, quero de alguma forma indicar ao compilador que realmente pretendo ter apenas um argumento de modelo, não um especificado e outro deduzido.

Posso fazer isso?

Gostaria de receber respostas para C ++ 11 e C ++ 14; As respostas C ++ 17 que envolvem guias de dedução são menos relevantes, mas se você tiver uma, publique-a (para versões futuras do NVCC ...)


Atualização: A própria biblioteca strf foi atualizada para contornar essa situação, mas a pergunta permanece conforme a pergunta.


11
Eu estou supondo que a aprovação de um iterador personalizado que envolve pouco, char*mas não seja, não é uma solução?
— Konrad Rudolph

11
@KonradRudolph: Essa é uma solução alternativa, mas não responde à minha pergunta. Na verdade, eu já tenho outra solução alternativa (específica para o que está no /*...*/), mas eu gostaria de pegar a estrada aqui.
— Einpoklum

11
Nesse caso, minha resposta (adivinhada) é "não pode ser feita", infelizmente. Para ser justo, não tenho certeza se aceitaria minha solução alternativa sugerida em meu próprio código.
— Konrad Rudolph

Apenas para esclarecer: você deseja uma solução geral que funcione sempre para diferenciar uma chamada entre sobrecargas de modelo com um vs dois parâmetros ou deseja apenas uma solução específica para este caso?
— Walnut #

@ walnut: solução geral seria melhor; meu cenário específico é principalmente motivação para o problema.
— Einpoklum

Respostas:


16
template<typename T>
inline constexpr auto range1_ptr = strf::range<T>;

template<typename T>
inline decltype(auto) range1(T begin, T end) {
    return range1_ptr<T>(begin, end);
}

Em seguida, ligue em range1vez de strf::range.

range1_ptr<T>(...)sempre pode ser usado para chamar explicitamente o modelo usando um argumento de modelo, mas não deduz os argumentos. range1replica a dedução do strf::rangemodelo original .

Isso funciona, porque [temp.deduct.funcaddr] / 1 diz que a dedução de argumento do modelo ao obter o endereço de uma função sem o tipo de destino da conversão é feita em cada modelo de função candidato como se o parâmetro e a lista de argumentos de uma chamada hipotética fossem esvaziar. Portanto, o segundo argumento do modelo não pode ser deduzido para a segunda sobrecarga com dois parâmetros de modelo. O único candidato que resta é a primeira sobrecarga, que será escolhida como o alvo do ponteiro da função.

Desde que não haja um segundo modelo de função candidato para o qual um ID de modelo válido com apenas um argumento possa ser formado, range1_ptrsempre pode ser usado para chamar o modelo de função usando um argumento sem ambiguidade. Caso contrário, a instanciação de range1_ptrdará um erro por causa da ambiguidade.


Não haverá ambiguidade strf::range<T>?
— Einpoklum

11
@einpoklum Compila bem no GCC e no Clang. Não verifiquei o padrão, mas ficaria surpreso se isso fosse ambíguo.
— noz

Talvez você deva mudar o nome da função para pretty_please_with_sugar_on_top()? ... C ++ pode ser tão esquisito às vezes ...
— einpoklum

Você conseguiu retirar a resposta aceita :-P
— einpoklum 08/01

11

Que tal passar por um using?

using tfp = void(*)(char const *, char const *);

tfp x = &strf::range;

char const * a = "abcd";

(*x)(a, a+2);

E isso compila? A segunda linha parece particularmente suspeita.
— Einpoklum

@einpoklum - engraçado, não é?
— max66

@einpoklum - infelizmente não é uma solução geral; funciona neste caso porque (se não estou errado) apenas a primeira range()versão é compatível tpf; outro caso pode ser diferente.
— max66

@einpoklum - na segunda linha, você também pode explicar o parâmetro template ( tfp x = &strf::range<char const *>;); Desta forma, eu suponho que você tem uma solução geral, quase equivalente a um da noz
— max66

0

Uma solução é

1) primeiro, você deve especificar o tipo para o segundo argumento, por exemplo (char *)(some_char_ptr + some_length)

2) não use constpara os dois, isso funciona bem:

strf::range((char *)some_char_ptr, (char *)(some_char_ptr + some_length));

Você pode tentar substituir (char *)por (const char *)à esquerda OU à direita, ainda funciona.


Isso é muito feio se os argumentos apontarem para constdados.
— aschepler

1. Não há um segundo argumento. Eu quero o modelo de argumento único. 2. Corte interessante! -1 para a primeira sugestão e +1 para a segunda :-P
— einpoklum
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.