模棱两可的过载模板


16

我有以下模板代码

#include <vector>
#include <array>
#include <iostream>

template<typename T1>
void foo(std::vector<T1> bar) {
    std::cout << "GENERIC" << std::endl;
}

template<typename T1>
void foo(std::vector<std::vector<T1>> bar) {
    std::cout << "SPECIFIC (vector)" << std::endl;
}

template<typename T1, int SIZE>
void foo(std::vector<std::array<T1, SIZE>> bar) {
    std::cout << "SPECIFIC (array)" << std::endl;
}

int main() {
    std::vector<std::vector<int>> a(2, std::vector<int> { 1, 2, 3});
    std::vector<std::array<int, 3>> b(2, std::array<int, 3> {4, 5, 6});

    foo(a);
    foo(b);
}

产生

SPECIFIC (vector)
GENERIC

我想知道为什么矢量模板版本使用特定模板调用,而矢量数组版本使用通用模板调用?


2
仅供参考:您可以vector在所有问题上都去除外表面,从而简化这一过程。看到这里
ChrisMM

@ChrisMM好抓。这个示例是从我的生产代码综合而来的,其中需要嵌套结构。
Xaser

5
MSVC调用数组矢量版本:godbolt.org/z/7Gfeb0
R2RT

Answers:


8
template<typename T1, size_t SIZE>
void foo(std::vector<std::array<T1, SIZE>> bar) {
    std::cout << "SPECIFIC (array)" << std::endl;
}

您应该使用std::size_t而不是int在这里跑

编辑: 实际上,您的评论和对代码的直觉使我深入探讨了该主题。乍一看,一个标准的开发(像我)期望编译器转换intstd::size_t(因为它们都是整体式和隐式转换非常简单),并选择void foo(std::vector<std::array<T1, SIZE>> bar)最佳的专业化。因此,在阅读模板参数推导页面时,我发现了这一点:

如果在参数列表中使用了非类型模板参数,并且推导了相应的模板参数,则推导的模板参数的类型(如其随附的模板参数列表中指定的那样,意味着保留引用)必须与完全非类型的模板参数,只是删除了cv限定词,并且从数组绑定推导出template参数的情况除外-在这种情况下,允许任何整数类型,即使bool始终会变为true:

当然,与往常一样,您必须阅读几次以上才能理解其含义:)

因此得出了一个有趣的结果。

已经没有选择我们想要的专业化,但是如果编译器被迫选择,这将是一个错误。

template<typename T1, int SIZE>
void foo(std::vector<std::array<T1, SIZE>> bar) {
    std::cout << "SPECIFIC (array)" << std::endl;
}

int main() {
    std::vector<std::array<int, 3>> b(2, std::array<int, 3> {4, 5, 6});

    foo(b); // P = std::vector<std::array<int,(int)SIZE>
            // A = std::vector<std::array<int,(unsigned_long)SIZE>>
            // error: deduced non-type template argument does not have the same
            // type as its corresponding template argument */
}

运行代码

另一个有趣的事情是:

如果未推导非类型模板参数,则不会存在强制参数和模板类型相同的限制。

#include <vector>
#include <array>
#include <iostream>

template<typename T1, int SIZE>
void foo(std::vector<std::array<T1, SIZE>> bar) {
    std::cout << "SPECIFIC (array)" << std::endl;
}

int main() {
    std::vector<std::array<int, 3>> b(2, std::array<int, 3> {4, 5, 6});

    foo<int,3>(b);
}

运行代码


@Xaser,因为数组的第二个模板参数的类型为size_t...
Jean-BaptisteYunès

2
考虑到R2RT的评论,似乎存在特定于编译器的差异。
Xaser

8

认为这仅仅是由于[temp.deduct.call]/4

通常,推导过程会尝试查找将使推导的A等于A的模板参数值

为了澄清,A是指参数,从[temp.deduct.call]/1

带有调用对应参数类型的模板参数推导(称为A)...

正如已经指出的那样,进行更改template<typename T1, int SIZE>template<typename T1, size_t SIZE>解决您遇到的问题。如中所述[temp.deduct.call]/4,编译器试图推导A与相同的A。由于std::array具有模板参数<class T, size_t N>(来自[array.syn]),因此它的第二个参数实际上是size_t,而不是int

因此,对于模板推导,您的泛型函数template<typename T1>能够完全匹配的类型A,而您的专业人士template<typename T1, int SIZE>则不完全匹配。我相信MSVC的推论不正确。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.