c++ - Variadic template 选择更通用的模板而不是重载

标签 c++ c++11 templates variadic-templates

想象一下这段代码:

#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;
}

首先打印输出:

DBL!!! 1.1 2 3.3 4

我的期望是,它会输出 DBL!!! 3.3 之前或没有 DBL!!!根本。但为什么是一个???

第二个打印输出:

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

关于c++ - Variadic template 选择更通用的模板而不是重载,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42557883/

相关文章:

c++ - 在 C++ 中存储派生类的实例

c++ - 可变长度数组 C++ 包装器容器

c++ - 为什么当用户提供 move 构造函数或 move 赋值时,复制构造函数和复制赋值将被删除?

c++ - 以下哪一项是 "auto"推导指针类型的更正确方法?

c++ - 用于线程的标准库版本 "Wait for Alert/Event"

c++ - 作者用下面的伪代码是什么意思?

c++ - 使用 fscanf 解析文件

c++ - 如何用固定元素直接初始化unordered_map?

html - Joomla 不在前端显示图像

c++ - 模板构造函数出现预期的不合格 ID 错误