Очень странная проблема, с которой я боролся последние несколько часов (после решения 5-6 других проблем с SFINAE, поскольку я новичок в ней). В основном в следующем коде я хочу иметь f()
работать для всех возможных конкретизации шаблона, но есть g()
доступны только при N == 2
:Почему SFINAE (enable_if) работает из определения внутреннего класса, но не извне
#include <type_traits>
#include <iostream>
template<typename T, int N>
class A
{
public:
void f(void);
void g(void);
};
template<typename T, int N>
inline void A<T, N>::f()
{
std::cout << "f()\n";
}
template<typename T, int N, typename std::enable_if<N == 2, void>::type* = nullptr>
inline void A<T, N>::g()
{
std::cout << "g()\n";
}
int main(int argc, char *argv[])
{
A<float, 2> obj;
obj.f();
obj.g();
return 0;
}
Когда я пытаюсь скомпилировать я получаю ошибку о том, 3 параметра шаблона вместо двух , Затем, после нескольких испытаний, я решил перенести определение g()
внутри определения A
самого, как это:
#include <type_traits>
#include <iostream>
template<typename T, int N>
class A
{
public:
void f(void);
template<typename t = T, int n = N, typename std::enable_if<N == 2, void>::type* = nullptr>
void g()
{
std::cout << "g()\n";
}
};
template<typename T, int N>
inline void A<T, N>::f()
{
std::cout << "f()\n";
}
int main(int argc, char *argv[])
{
A<float, 2> obj;
obj.f();
obj.g();
return 0;
}
Теперь, волшебно все работает. Но мой вопрос: ПОЧЕМУ? Разве компилятор не видит, что внутри определения класса я пытаюсь встроить функцию-член, которая также зависит от трех параметров шаблона? Или давайте обратим вопрос: если он работает внутри определения A
, почему он не работает на улице? Где разница? Разве нет еще 3 параметра, что на +1 больше, чем класс A
для его параметров шаблона?
Кроме того, почему это работает, когда я делаю третий параметр не-тип один, а не тип один? Заметьте, что я действительно создаю указатель типа, возвращаемого enable_if, и присваиваю ему значение по умолчанию nullptr, но я вижу, что я не могу просто оставить его там как параметр типа, как в других сообщениях форума SO, которые я вижу здесь.
Цените его так много, спасибо !!!
Также обратите внимание, что, как показано в [skypjack's answer] (http://stackoverflow.com/a/41904229/5386374): 1) 'std :: enable_if_t' может использоваться как псевдоним типа для 'typename std :: enable_if :: type', 2) 'T' по умолчанию' void' и может быть опущен, если он не должен быть другим типом, и 3) 'std :: enable_if_t' может использоваться для возвращаемого типа функции (или на параметр, если функция имеет какой-либо). –
Большое вам спасибо !!! Я все еще перевариваю этот кусок информации и переписываю то, что знал о шаблонах, теперь, когда я сталкиваюсь с этим никогда не замеченным миром SFINAE :) Кстати, знаете ли вы, почему MSVC дает мне ошибки везде, говоря «тип ': не является членом' std :: enable_if '??? Код, который я опубликовал, был скомпилирован с помощью некоторой онлайн-среды, основанной на gcc, но мой фактический проект находится в VC, и я вижу, что это как-то предупреждает меня о случае, когда чек будет ложным -> который duuhhh, вот почему я делаю для этого, к ТАКЖЕ имеют случаи, когда чек не пройдет. Любая идея, почему он идиот? :) –
Otringal
@Otringal Вы определили версию 'g()', которую можно вызвать, когда 'N! = 2'? Если вы этого не сделали, вы получите сообщение об ошибке при попытке вызвать 'g()' на 'A', чье 'N' - это что-то другое, кроме 2, потому что' std :: enable_if' имеет только член 'type', если логическое условие' true'. –