variadic模板选择了更多常见的模板而不是过载

Variadic template chooses more common template instead of overload

本文关键字:选择 variadic 常见      更新时间:2023-10-16

想象此代码:

#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

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()的呼叫只有两个候选者:nullary函数(#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...);
    }
    

您有可见性问题,您可以通过向前声明进行修复:

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...);
}

演示

更简单的是只有特定于简单印刷品的特定,并将递归分开,类似于:

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