2015-03-29 24 views
6

Próbowałem zrobić logger przy użyciu szablonów variadic C++ 11, ale to nie działa dla std::endl, ponieważ std::endl jest funkcją szablonu, a kompilator nie wie, jaką specjalizację z std::endl, aby wybrać. Czy istnieje sposób, w jaki mogę wymusić na zawsze wybranie std::endl<char, std::char_traits<char>>? Jeśli to możliwe, chcę użyć bezpośrednio std :: endl.C++ 11 szablony variadic i std :: endl

EDIT: wygląda na to, że nie jest obecnie możliwe w C++ 11 i i najlepszym sposobem jest użycie #define lub co vsoftco odpowiedział.

#include <iostream> 
#include <string> 

class Logger { 

public: 

    template<typename T> 
    void log(T val); 

    template <typename T, typename ...Args> 
    void log(T val, Args... args); 

}; 

// explicit specialization not working 
template<> 
void Logger::log(std::basic_ostream<char, std::char_traits<char>> (*modifier) (std::basic_ostream<char, std::char_traits<char>>)) { 

    std::cout << modifier; 

} 

template<typename T> 
void Logger::log(T val) { 

    std::cout << val; 

} 

template<typename T, typename ...Args> 
void Logger::log(T val, Args... args) { 

    log(val); 
    log(args...); 

} 

int main(int argc, char* argv[]) 
{ 
    Logger log; 

    log.log("Nazdar ", "bazar ", "cau", std::endl, "kik"); // ERROR: cannot determine which instance of function template "std::endl" is intended 
    log.log("Nazdar ", "bazar ", "cau", std::endl<char, std::char_traits<char>>, "kik"); 

    std::cin.get(); 

    return 0; 
} 
+0

@vsoftco: 'std :: flush' ma ten sam problem co' std :: endl '. –

+0

@vsoftco: to użycie nie zadziała, ponieważ 'std :: endl' nie jest typem, jest to funkcja szablonu – Krab

+0

@Krab, przepraszam, że zepsułem to wszystko, tak, jesteś absolutnie poprawny. – vsoftco

Odpowiedz

2

wpadłem na to, w zasadzie ponownego zdefiniowania std::endl poprzez niestandardowy otoki my_endl robienie domyślnymi parametrami szablonu. Nie najbardziej elegancki, ale spełnia swoją rolę. Oczywiście, dla większej liczby takich manipulatorów, należy napisać specjalistyczne opakowanie, ale myślę, że nawet to może być możliwe dzięki bardziej sprytnej implementacji.

#include <iostream> 
#include <string> 
#include <type_traits> 

class Logger { 

public: 

    template<typename T> 
    void log(T val); 

    template <typename T, typename ...Args> 
    void log(T val, Args... args); 
}; 

template<typename T> 
void Logger::log(T val) { 
    std::cout << val; 
} 

template<typename T, typename ...Args> 
void Logger::log(T val, Args... args) { 

    log(val); 
    log(args...); 

} 

template< class CharT = char, class Traits = std::char_traits<CharT> > 
inline std::basic_ostream<CharT, Traits>& my_endl(std::basic_ostream<CharT, Traits>& os) 
{ 
    return std::endl(os); 
} 

// or, use the excellent (and better) suggestion by 0x499..., 
// auto manip = std::endl<char, std::char_traits<char>>; 
// log.log(..., manip) 


int main(int argc, char* argv[]) 
{ 
    Logger log; 

    log.log("Nazdar ", "bazar ", "cau", my_endl<>, "kik"); // ERROR: cannot determine which instance of function template "std::endl" is intended 
    log.log("Nazdar ", "bazar ", "cau", my_endl<>, "kik"); 

    std::cin.get(); 

    return 0; 
} 
+0

po prostu chcę wspomnieć, że @ 0x499 ... odpowiedź jest bardziej elegancka i nieco lepsza, ponieważ pozwala uniknąć wywołania dodatkowego wywołania funkcji (oczywiście, może zrobić mój inline, ale powinieneś naprawdę użyć 'auto' w tym przypadku) . – vsoftco

+0

@ faktycznie chciałem użyć tego 'std :: endl', ale prawdopodobnie nie jest to możliwe. W takim przypadku '# define' może być lepszym sposobem, ponieważ nie przydziela miejsca. – Krab

+0

@Krab tak, myślę, że nie jest możliwe (choć nigdy nie mów nigdy) używanie 'std :: endl'. Przynajmniej nie mogę zobaczyć, jak to zrobić ... – vsoftco

5

Prostszym rozwiązaniem, aby osiągnąć ten sam cel:

// global or class member 
enum MyEndl { my_endl }; 

// class member function 
void log(MyEndl x) { std::cout << std::endl; } 

Wykorzystanie:

log.log("Nazdar ", "bazar ", "cau", my_endl, "kik"); 
+0

to jest również zadbane! +1 Ale co z wysyłaniem '0' do' log.log (...) '? Nie używasz "klasy enum", nie dostaniesz dwuznaczności? Mam na myśli, że nie wyświetlisz '0', ale' enum' będzie lepiej pasować. Tymczasowo wycofuję +1 :) – vsoftco

+0

@vsoftco Nie mam żadnych niejednoznaczności podczas kompilacji i wybieram wersję szablonu dla '0' (choć nie jestem pewien dokładnych reguł) –

+0

Nie, myślę, że' wyliczenie 'będzie lepiej pasować, ale co jeśli chcesz wyświetlić' 0'? Twoje rozwiązanie musi używać "0" zamiast liczby całkowitej "0". Oczywiście, możesz użyć jakiejś szalonej nieprawdopodobnej liczby, takiej jak "-18947618723" jako początku wyliczenia. – vsoftco

Powiązane problemy