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.

einpoklum
fonte
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.

noz
fonte
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);
max66
fonte
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.

tontonCD
fonte
Isso é muito feio se os argumentos apontarem para constdados.
aschepler
1. Não 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