2012-10-28 21 views
10

Przyjmijmy następujący kod:Nieoczekiwany przeciążenie wywoływana, gdy funkcja jest wywoływana z zmiennej liczbie argumentów szablonu konkretyzacji

#include <iostream> 

template<typename... T> 
void foo(const T &...); 

template<unsigned N> 
void foo(const char (&)[N]) 
{ 
    std::cout << "char(&)[N]" << std::endl; 
} 

void foo(const char *) 
{ 
    std::cout << "const char *" << std::endl; 
} 

template<typename T> 
void foo(const T &) 
{ 
    std::cout << "Single" << std::endl; 
} 

template<typename First, typename... T> 
void foo(const First & first, const T &... rest) 
{ 
    std::cout << "Generic + " << sizeof...(T) << std::endl; 
    foo(first); 
    foo(rest...); 
} 

int main() 
{ 
    const char * c = "asdf"; 
    char a[] = {'a', 'b', 'c', 'd'}; 
    foo('f', c, a, 1); 
    foo(a); 
} 

The output is:

Generic + 3 
Single    // fine; 'f' is `char` -> generic 
Generic + 2 
const char *  // fine; c is `const char *` 
Generic + 1 
const char *  // (!) not fine 
Single 
char(&)[N]   // fine; a is char[4] 

Ostatni telefon - foo(a), gdzie a jest char[4] - zwraca wersję jestem oczekiwanie - template<unsigned N> void foo(const char (&)[N]). Ale dlaczego nie instancję szablonu variadic z foo dzwoni foo(const char (&)[N], ale zamiast tego wywołuje foo(const char *)? Jeśli nie było przeciążenia tablicy znaków, należy się tego spodziewać - ale dlaczego tak się dzieje? Czy nie powinien poprawnie przechwytywać typu tablicowego const First &?

Jaki byłby najprostszy sposób, aby ogólna wersja variadic działała poprawnie z tablicami przekazanymi do niej?


Jak Matthieu M. zauważył w komentarzach, problem prawdopodobnie nie jest spowodowany przez szablony o zmiennej liczbie argumentów, ale indirection:

#include <iostream> 

template <unsigned N> 
void foo(const char (&)[N]) 
{ 
    std::cout << "char(&)[N]" << std::endl; 
} 

void foo(const char *) 
{ 
    std::cout << "const char *" << std::endl; 
} 

template <typename T> 
void goo(T const& t) { 
    foo(t); 
} 

int main() 
{ 
    char a[] = {'a', 'b', 'c', 'd'}; 
    foo(a); 
    goo(a); 
} 
 
    char(&)[N] 
    const char * 

Powiedział też, że może to być kompilator bug - chociaż kod daje dokładnie takie same wyniki zarówno w devie Clang 3.2, G ++ 4.6 i 4.7.

R. Martinho Fernandes zauważyć, że zmiana a „s typu w poprzednim fragmencie do const char a[] sprawia kodu dostarcza const char * dwukrotnie.

+1

Dlaczego '// dobrze; "afas" to const char *? Nie jest! http://ideone.com/4KewDe –

+0

@ R.MartinhoFernandes, poprawiono. – Griwes

+2

Udało mi się jeszcze bardziej zmniejszyć problem [tutaj] (http://liveworkspace.org/code/72b3963b4f0c2d00592b4ce00f08fc82). Najwyraźniej przyczyną jest ten problem, a variadic nie ma z tym nic wspólnego. Wciąż nie znalazłem prawdopodobnego wyjaśnienia ... To z pewnością wygląda na błąd kompilatora. –

Odpowiedz

5

Myślę, że mogę odpowiedzieć na drugą sekcję: Jak to naprawić? Nie jestem pewien, czy otrzymałem właściwą odpowiedź, ponieważ przypadek podania ciągu literalnego powoduje, moim zdaniem, wciąż zły wynik. Podstawowym pomysłem na poprawkę jest użycie idealnego przekazywania, zamiast mieć nadzieję, że właściwy typ zostanie wydedukowany dla T const&. Dlaczego użycie T const& z tablicą powoduje, że macierz ulega rozkładowi, ale nie do końca rozumiem.

Oczywiście korzystanie z funkcji "do przodu" oznacza, że ​​funkcja ta jest idealnie dopasowana, a niektóre specjalizacje faktycznie dokonują pewnych konwersji, co najmniej, dodając const. Dlatego konieczne jest użycie innej nazwy. W sumie wygląda to następująco:

#include <iostream> 
#include <utility> 

void foo() 
{ 
} 

template<unsigned N> 
void special(const char (&)[N]) 
{ 
    std::cout << "char(&)[" << N << "]" << '\n'; 
} 

void special(const char *) 
{ 
    std::cout << "const char *" << '\n'; 
} 

template<typename T> 
void special(const T &) 
{ 
    std::cout << "Single" << '\n'; 
} 

template<typename First, typename... T> 
void foo(First&& first, T&&... rest) 
{ 
    std::cout << "Generic + " << sizeof...(T) << '\n'; 
    special(std::forward<First>(first)); 
    foo(std::forward<T>(rest)...); 
} 

int main() 
{ 
    char const* c("foo"); 
    char a[] = {'a', 'b', 'c', 'd'}; 
    foo('f', "afas", a, c, 1); 
    foo(a); 
} 
Powiązane problemy