2012-12-13 21 views
15

Sto provando a creare una classe a modelli variadic che fornisce un metodo per ogni classe nella lista di caratteri. Un esempio è mostrato sotto che crea un metodo print per ogni classe nella typelist:Funzione ambigua Chiamate alle classi di base C++

#include <iostream> 
#include <string> 

// Helper class providing a function call 
template <typename T> 
class PrintHelper 
{ 
public: 
    void print(const T& t) { std::cout << t << std::endl; } 
}; 

// Provides a print method for each type listed 
template <typename... Ts> 
class Printer : public PrintHelper<Ts>... 
{}; 

int main() 
{ 
    Printer<int, std::string> p; 
    p.print(std::string("Hello World")); // Ambiguous Call 
} 

I risultati pubblicitari commentati un errore da GCC 4.6.3 sulla linea commentata. Qual è il modo corretto per risolvere l'ambiguità o dovrei guardare ad un design diverso?

+0

Stesso problema con gcc 4.7.2 (in [liveworkspace (http://liveworkspace.org/code/1a4YN8$0)). Grazie per l'esempio copy/pastable btw. –

+1

clang 3.2 trunk 165721 dice: 'errore: membro 'print' trovato in più classi base di diversi tipi' – user786653

+2

Vedere anche [questa domanda] (http://stackoverflow.com/questions/5368862/why-do-multiple-edherited -Funzioni-con-stesso-nome-ma-diverse-firme-no). Questo è essenzialmente un problema della combinazione di regole nascoste e delle funzionalità molto limitate di decomprimere argomenti di template variadic. –

risposta

8

Non mi piace rispondere alla mia domanda, ma ho creato la seguente soluzione dai commenti qui. Porta tutte le funzioni print in ambito e consente la risoluzione di sovraccarico C++ su tutte le funzioni.

#include <iostream> 
#include <string> 

// Helper class providing a function call 
template <typename T> 
class PrintHelper 
{ 
public: 
    void print(const T& t) { std::cout << t << std::endl; } 
}; 

// Provides a print method for each type listed 
template <typename... Ts> 
class Printer 
{}; 

template<typename T> 
class Printer<T> : public PrintHelper<T> 
{ 
public: 
    using PrintHelper<T>::print; 
}; 

template<typename T, typename... Ts> 
class Printer<T, Ts...>: public PrintHelper<T>, public Printer<Ts...> 
{ 
public: 
    using PrintHelper<T>::print; 
    using Printer<Ts...>::print; 
}; 

int main() 
{ 
    Printer<int, std::string> p; 
    p.print("Hello World"); // Not an ambiguous Call 
} 
+0

+1 È fantastico, perché non ci ho pensato ... Quindi immagino che è così che puoi decomprimere le dichiarazioni d'uso per i modelli variadici. –

+0

Va perfettamente bene rispondere alla tua stessa domanda (e anche incoraggiata esplicitamente). È redditizio per i futuri lettori. Cf. http://blog.stackoverflow.com/2011/07/its-ok-to-ask-and-answer-your-own-questions/. Anche la soluzione che proponi dovrebbe essere accettata. –

9

Per risolvere l'ambiguità, è possibile fare

template <typename... Ts> 
struct Printer : PrintHelper<Ts>... 
{ 
    template <typename U> 
    void print (const U& t) 
    { 
     PrintHelper<U>::print (t); 
    } 
}; 

(vedi an example)

, ma questo non è così robusta come si potrebbe sperare. In particolare, non è possibile stampare un oggetto che è convertibile in uno dei tipi dall'elenco dei tipi.

Con alcuni modelli con metaprogrammazione, è comunque possibile inviare alla stampante corretta. Per fare questo è necessario selezionare un tipo da Ts... a cui è convertibile U e chiamare il diritto PrintHelper, vale a dire.

PrintHelper<typename find_convertible<U, Ts...>::type>::print (t); 

dove find_convertible<U, Ts...> è definito da

template <typename U, typename... Ts> 
struct find_convertible 
{}; 

template <typename U, typename V, typename... Ts> 
struct find_convertible<U, V, Ts...> : 
    std::conditional< 
     std::is_convertible<U, V>::value, 
     std::common_type<V>, // Aka identity 
     find_convertible<U, Ts...> 
    >::type 
{}; 

(vedi example)

+0

Bella pace del codice! –

+0

Grazie per l'aiuto. Il primo esempio è sufficiente per il mio caso d'uso. A seguito di uno dei commenti sopra c'è un'altra potenziale soluzione per consentire le conversioni modificando l'ereditarietà (http://liveworkspace.org/code/2PpiGS$1). Questo userà la risoluzione di sovraccarico C++ in constrast sul metodo che prenderà il primo tipo convertibile nella lista di caratteri. –

+0

@PeterOgden: Mi piace molto il tuo altro metodo. Potresti postarlo come risposta per riferimento futuro? Gli imballaggi in linea non durano per sempre. –

3

Il seguente codice in grado di risolvere il problema dell'ambiguità:

#include <iostream> 
#include <string> 

// Helper class providing a function call 
template <typename T> 
class PrintHelper 
{ 
    protected: 
    void print_impl(const T& t) { std::cout << t << std::endl; } 
}; 

// Provides a print method for each type listed 
template <typename... Ts> 
class Printer : public PrintHelper<Ts>... 
{ 
    public: 
    template <typename U> 
    void print(const U& u) { 
     PrintHelper<U>::print_impl(u); 
    }; 
}; 

int main() 
{ 
    Printer<int, std::string> p; 
    p.print(std::string("Hello World")); // Ambiguous Call 
} 

Il che non è molto bello a causa di il requisito che il tipo U (dedotto in chiamata) sia esattamente uno di th e tipi nella lista dei tipi variadici. Potresti essere in grado di fantasticare un po 'per risolvere quel problema. Con un po 'di template magic e Sfinae, puoi probabilmente risolverlo abbastanza facilmente (ma non è sicuramente così pulito e ordinato).

Il problema dell'ambiguità non è correlato all'utilizzo di modelli template o variadic, ovviamente è un'applicazione diretta delle regole di ricerca dei membri (Sezione 10.2/2 dello standard), ovvero, il cosiddetto "regole per nascondere i membri". Se si prende questa versione non-modello più semplice, si otterrà lo stesso problema di ambiguità, ma con una soluzione molto semplice ad esso:

struct IntPrinter { 
    void print(const int& i) { std::cout << i << std::endl; }; 
}; 

struct StringPrinter { 
    void print(const std::string& s) { std::cout << s << std::endl; }; 
}; 

struct IntStringPrinter : IntPrinter, StringPrinter { 
    using IntPrinter::print;  // These using-statements will solve the problem 
    using StringPrinter::print; // by importing all 'print' functions to the same 
           // overload resolution level. 
}; 

Quindi, il problema qui è davvero che l'ambiguità si pone prima il compilatore anche tentativi applicare le normali regole di risoluzione del sovraccarico, perché prima cerca di capire quale ramo dell'ereditarietà seguire per trovare le funzioni membro, e quindi risolverà il sovraccarico solo a livello di ereditarietà. E il problema quando si usano i modelli variadic è che non sembra esserci un modo per decomprimere un insieme di istruzioni "using" per importare tutte le funzioni di stampa fino allo stesso livello di ereditarietà. Se qualcuno conosce un modo per scartare tali dichiarazioni d'uso, sono tutto orecchie. Potrebbe essere necessario ricorrere a soluzioni di modelli pre-variadici (come un modello generale con 10 argomenti e specializzato per tutte e dieci le versioni).

Problemi correlati