2017-03-02 56 views
3

想象一下這樣的代碼:可變參數模板選擇比較常見的模板,而不是超載

#include <iostream> 
void PrintInternal() { 
    std::cout << std::endl; 
} 

template <typename T, typename...ARGS> 
void PrintInternal(const T& head, const ARGS&...rest) { 
    std::cout << head << " "; 
    PrintInternal(rest...); 
}; 

template <typename...ARGS> 
void PrintInternal(const double& head, const ARGS&...rest) { 
    std::cout << "DBL!!! " << head << " "; 
    PrintInternal(rest...); 
} 

template <typename...ARGS> 
void Print(const ARGS&...args) { 
    PrintInternal(args...); 
} 

int main() { 
    Print(1.1, 2, 3.3, 4); 
    Print(0, 1.1, 2, 3.3, 4); 
    return 0; 
} 

首先Print輸出:

DBL! 1.1 2 3.3 4

我的期望是,它會輸出DBL! 3.3之前還是沒有DBL!在所有。但爲什麼一個?

Print輸出:

0 1.1 2 3.3 4

爲什麼沒有DBL !!!如果我們在第一個例子中有一個,那麼輸出就像是一樣。

以及如何實現,每個double我會輸出一些不同的部分專業化?我想,這簡單的重載應該沒問題......

鏈接cpp.sh查看編譯結果 - >http://cpp.sh/42cz

+3

仔細看看,小心。第二個打印有0作爲第一個參數,對不對? (0假設有整數類型)。你的「DBL !!」將第一個參數作爲double。 說,如果你試過0或0.0它會工作 –

+0

是的,你在這裏是正確的,謝謝,但是,更多的奧祕仍未解決:) – Starl1ght

+0

模板函數的順序是重要的。在調用之前,模板調用的所有函數都需要可見。 – NathanOliver

回答

6

查找的PrintInternal()是要找到的功能兩種類型:

  • 所有的函數模板定義的點看到的功能。
  • 函數模板的相關參數的關聯名稱空間中的所有函數。

在這種情況下,我們所有的參數都是基本類型,所以沒有任何關聯的命名空間。這使事情變得更容易。因此,當我們開始:

#include <iostream> 
void PrintInternal() { // #1 
    std::cout << std::endl; 
} 

template <typename T, typename...ARGS> 
void PrintInternal(const T& head, const ARGS&...rest) { // #2 
    std::cout << head << " "; 
    PrintInternal(rest...); // <== (*) 
}; 

template <typename...ARGS> 
void PrintInternal(const double& head, const ARGS&...rest) { // #3 
    std::cout << "DBL!!! " << head << " "; 
    PrintInternal(rest...); 
} 

這標誌着調用PrintInteral()只有兩個候選人:無元函數(#1)和自身(#2)。另一個,更專業的PrintInteral()需要const double&(#3)尚不可見,因此永遠不會被視爲候選人。並不是#2比3更受歡迎,只是它是唯一的選擇。

如果你翻轉兩個重載的順序,那麼你會遇到一個不同的問題 - 你將無法找到#2!

這給你幾個選擇:

  1. 分離出來,打印從打印的所有元素的單個元素。這樣,你只需要重載PrintSingle(),這很容易做到。
  2. 正向聲明所有的函數模板,以使它們全部可見。
  3. 另外一個論點僅僅是爲了第二個要點在頂部應用。只是存在一個虛擬參數,僅用於使用ADL進行名稱查找。該解決方案有時是必要的,但總是迷惑:

    namespace N { 
        struct adl { }; 
    
        void PrintInternal(adl) { 
         std::cout << std::endl; 
        } 
    
        template <typename T, typename...ARGS> 
        void PrintInternal(adl, const T& head, const ARGS&...rest) { 
         std::cout << head << " "; 
         PrintInternal(adl{}, rest...); 
        } 
    
        template <typename...ARGS> 
        void PrintInternal(adl, const double& head, const ARGS&...rest) { 
         std::cout << "DBL!!! " << head << " "; 
         PrintInternal(adl{}, rest...); 
        } 
    } 
    
    template <typename...ARGS> 
    void Print(const ARGS&...args) { 
        PrintInternal(N::adl{}, args...); 
    } 
    
+0

爲什麼翻轉排序隱藏自己的定義? – Jarod42

+0

@ Jarod42如果'T'超載先行,則無法看到'double'。如果'雙'先走,就看不到'T'。同樣的問題無論如何。 – Barry

+0

是的,我們只需要轉發聲明函數。 – Jarod42

0

你有知名度的問題,你可以通過向前聲明修復:

template <typename...ARGS> void PrintInternal(const double& head, const ARGS&...rest); 

template <typename T, typename...ARGS> 
void PrintInternal(const T& head, const ARGS&...rest) { 
    std::cout << head << " "; 
    PrintInternal(rest...); 
} 

template <typename...ARGS> 
void PrintInternal(const double& head, const ARGS&...rest) { 
    std::cout << "DBL!!! " << head << " "; 
    PrintInternal(rest...); 
} 

Demo

簡單

將有具體的只對簡單的打印,並具有遞歸分開,像:

void printSingle(double d) 
{ 
    std::cout << "DBL!!! " << d << " "; 
} 

template <typename T> 
void printSingle(const T& t) 
{ 
    std::cout << t << " "; 
} 

template <typename...ARGS> 
void Print(const ARGS&...args) { 
    const int dummy[] = {0, (printSingle(args), 0)...}; 
    static_cast<void>(dummy); // Avoid warning for unused variable 
    std::cout << std::endl; 
} 

Demo

+0

DBL !!! 1.1 DBL !!! 2 DBL !!! 3.3 DBL !!! 4 這是一個不好的輸出。看起來像模板根本不起作用,只有雙重過載 – Starl1ght

+0

更改排序只會改變哪一個不按預期工作。 – Barry

+0

@ Starl1ght:的確,答案已修正。 – Jarod42