C++ Шаблонная специализация по типу нетипичного параметра

Допустимо ли иметь варианты одной и той же шаблонной функции, которые различаются по типу нетипичного члена?

template<typename T, unsigned int V>
void f(unsigned int& v) { v = V; }

template<typename T, bool B>
void f(bool& b) { b = B; }

Намерение состоит в том, чтобы можно было

unsigned int meaningOfLife;
f<sometype, 42>(meaningOfLife);

bool areYouAlive;
f<sometype, true>(areYouAlive);

clang и gcc молчат, но отчеты MSVC

warning C4305: 'specialization': truncation from 'int' to 'bool'

Я бы хотел избежать указания типа константы:

f<sometype, bool, true>

и хотим убедиться, что постоянное значение и целевое значение совпадают.

---- Макве ----

#include <iostream>

template<unsigned int V>
void f(unsigned int& v) { v = V; }

template<bool B>
void f(bool& b) { b = B; }

int main()
{
    unsigned int u { 0 };
    bool b { false };

    f<42>(u);
    f<true>(b);

    std::cout << u << b;
}

Пример Rextester: http://rextester.com/VIGNP16100

Warning(s):
source_file.cpp(14): warning C4305: 'specialization': truncation from 'int' to 'bool'
/LIBPATH:C:\boost_1_60_0\stage\lib 
Microsoft (R) C/C++ Optimizing Compiler Version 19.00.23506 for x64
421

1 ответ

Решение

Краткий ответ: код в порядке, и MSVC выдает ложное предупреждение.

MSVC и g ++ имеют ошибки в сопоставлении аргументов не типового шаблона, но они выбирают правильный для вашего конкретного примера.


Длинный ответ: нормально иметь перегруженные шаблоны функций с нетиповыми параметрами шаблона.

Однако сопоставление аргумента шаблона с объявлением шаблона не работает, как можно было ожидать (во всяком случае, для меня). ВСЕ соответствующие шаблоны вводятся в разрешении перегрузки. На любом этапе он не предпочитает "точное совпадение".

Согласно C++17 [temp.arg.nontype/]2, преобразованные константные выражения разрешены. Это означает, например:

  • 42 Матчи int а также unsigned int,
  • 42u Матчи int а также unsigned int,
  • 1u Матчи unsigned int, int а также bool,

Обратите внимание, что преобразованное константное выражение не может содержать сужающее преобразование, и int в bool сужается, если значение не является постоянным выражением значения 0 или же 1, Так 42 не совпадает bool, (Ссылка: C++17 [expr.const]/4).

Если бы у нас была следующая настройка:

template<unsigned int V> void g() {}
template<bool B> void g() {}

тогда правильное поведение:

  • g<42>() звонки g<unsigned int>,
  • g<1>() неоднозначно.
  • g<1u>() неоднозначно.

MSVC 2017 и g++ 7,8 все некорректно позволяют g<42> соответствовать g<bool>и сообщить g<42> как неоднозначно.

MSVC выдает предупреждение, которое вы видите во время генерации неверного соответствия; g ++ не дает никакой диагностики вообще. Если мы удалим unsigned int перегрузка затем g ++ молча принимает неверный код без диагностики.


В вашем коде есть неконстантный ссылочный параметр lvalue:

template<unsigned int V>  void h(unsigned int&) {}
template<bool B>          void h(bool&) {}

Это имеет значение, потому что разрешение перегрузки может сделать выбор на основе аргумента функции. Для звонка:

unsigned int m;
h<1u>(m);

тогда обе перегрузки h вводятся в разрешение перегрузки, однако затем h<unsigned int> выигрывает потому что h<bool>(m) будет недействительным.

Как уже говорилось выше, вызов h<42>(m); выигрывает на первом этапе, потому что это не может сравниться h<bool>; но в MSVC++ (и g++) это неправильно позволяет h<bool> чтобы пройти на этом этапе, но обрезает его позже, как для h<1u> дело.

Другие вопросы по тегам