Неоднозначні шаблони перевантаження


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
FYI: Ви можете спростити це, з тієї ж проблеми, видаливши зовнішню vectorна всіх. Дивіться тут
ChrisMM

@ChrisMM хороший улов. Цей приклад був синтезований з мого виробничого коду, де необхідна вкладена структура.
Xaser

5
MSVC називає версію векторних масивів: godbolt.org/z/7Gfeb0
R2RT

Відповіді:


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. біжи сюди

Редагувати: Власне, ваші коментарі та моя інтуїція щодо коду змусили мене зануритися в тему. На перший погляд, стандартний розробник (як я) очікує, що компілятор перетвориться intна std::size_t(оскільки вони є цілісним типом і неявно перетворюється дуже тривіально) і вибере void foo(std::vector<std::array<T1, SIZE>> bar)найкращу спеціалізацію. Отже, читаючи сторінку виведення аргументів шаблону, я виявив це:

Якщо параметр шаблону нетипового типу використовується в списку параметрів і виводиться відповідний аргумент шаблону, тип виведеного аргументу шаблону (як зазначено в його вкладеному списку параметрів шаблону, тобто посилання зберігаються) повинен відповідати типу параметр шаблону нетипового шаблону, за винятком того, що cv-класифікатори випадають, і за винятком випадків, коли аргумент шаблону виводиться із зв'язаного масиву - у цьому випадку дозволений будь-який інтегральний тип, навіть bool, хоча він завжди стане істинним:

Як завжди, звичайно, ви повинні прочитати ще кілька разів, ніж раз, щоб зрозуміти, що це означає :)

Так виходить цікавий результат.

Наша бажана спеціалізація вже не обрана, але якби компілятор був змушений вибрати, це була б помилка.

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-Baptiste Yunè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 є неправильним у своєму виведенні.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.