Почему мы не находим правильную перегрузку оператора при использовании макроса?
Я пишу шаблон класса, который принимает произвольный указатель на функцию в качестве аргумента нетипичного шаблона. Я хотел бы использовать
template <auto F> struct Foo;
но мой компилятор (MSVC 2017.5) не поддерживает auto
в списке параметров шаблона (хотя он поддерживает многие функции C++17). Итак, я написал взломать это как-то так:
template <typename T>
using Func = void (*)(T);
template <typename TF, TF F> struct Foo;
template <typename T, Func<T> F>
struct Foo<Func<T>, F> { ... };
#define FOO(func) Foo<decltype(func), func>
Я реализовал потоковый оператор (для QDebug
или любой другой текстовый поток), как это:
template <typename T, Func<T> F>
QDebug &operator <<(QDebug &out, const FOO(F) &foo)
{
...
return out;
}
Но мой основной код не может найти право operator<<
перегрузка:
void func(int) { ... }
...
FOO(&func) foo;
qDebug() << foo; // error
Удивительно, но все работает при определении оператора как
QDebug &operator <<(QDebug &out, const Foo<Func<T>, F> &foo)
// ^^^^^^^^^^^^^^^
Кажется, что Func<T>
из этой последней строки и decltype<F>
из макроса не дают одинаковый тип. Но я проверил это и std::is_same_v<Func<int>, decltype(&func)>
дает правду. Я не могу понять, почему с помощью макроса FOO
дает мне любое другое поведение времени компиляции, как если бы я его не использовал.
Минимальный рабочий пример:
#include <iostream>
template <typename T>
using Func = void (*)(T);
template <typename TF, TF F> struct Foo;
template <typename T, Func<T> F>
struct Foo<Func<T>, F> { };
#define FOO(func) Foo<decltype(func), func>
template <typename T, Func<T> F>
std::ostream &operator <<(std::ostream &out, const FOO(F) &foo)
// std::ostream &operator <<(std::ostream &out, const Foo<Func<T>,F> &foo)
{
return out;
}
void func(int);
int main(int argc, char **argv)
{
FOO(&func) foo;
std::cout << foo << std::endl; // error
}
3 ответа
Как часть template auto
на бумаге, мы также получили новое правило вывода в [temp.deduct.type]:
Когда значение аргумента соответствует не типу параметра шаблона
P
который объявлен с зависимым типом, выводится из выражения, параметры шаблона в типеP
выводятся из типа значения.
Это правило позволяет следующему примеру работать в C++17, потому что мы можем вывести T
от типа V
:
template <typename T, T V>
struct constant { };
template <typename T, T V>
void foo(constant<decltype(V), V> ) { }
int main() {
foo(constant<int, 4>{});
}
В C++14 и более ранних версиях этот пример плохо сформирован, потому что T
не выводится Именно это поведение вы пытаетесь использовать (неявно) при использовании этого макроса, который расширяется до:
template <typename T, Func<T> F>
std::ostream &operator <<(std::ostream &out, const Foo<decltype(F), F> &foo);
Вы пытаетесь вывести T
от F
, Поскольку MSVC не поддерживает template auto
тем не менее, это, возможно, неудивительно, что он также не поддерживает другие части машины, необходимые для template auto
Работа. И в этом разница между макросом и немакро-альтернативой, которая может просто вывести T
:
template <typename T, Func<T> F>
std::ostream &operator <<(std::ostream &out, const Foo<Func<T>,F> &foo)
Итак, простое решение - просто... не использовать макрос, так как у вас есть рабочая форма, даже более многословная. Более долгое решение - использовать ответ Якка, который полностью обходит весь вопрос о выводе.
Обходной путь:
template <typename T, Func<T> F>
struct Foo<Func<T>, F> {
friend QDebug &operator <<(QDebug &out, const Foo &foo){
...
return out;
}
};
Когда макрос не используется, второй параметр шаблона F
может быть выведен из второго аргумента функции foo
, Когда используется макрос, второй параметр шаблона F
не может быть выведен из второго аргумента функции, потому что он появится внутри decltype
: Foo<decltype(F), F> & foo
, Ваш код может быть упрощен до
template<typename T>
void f(decltype(T) v){}
int v{};
f(v);
Компилятор знает тип аргумента (int
) однако параметр шаблона T
не может быть выведено из известного типа аргумента, потому что при использовании внутри decltype
T
должны быть известны заранее.