2017-03-02 53 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