Piszę klasę, która ma kilka różnych funkcji z std::function
(a przynajmniej klasy są pod wieloma względami podobne). Jak wszyscy wiecie, std::function
tworzy instancję, określając parametry szablonu (tj. std::function<void (std::string&)>
), jest taka sama dla mojej klasy. Mam jednak wyjątek, chcę specjalizować w klasie jedną funkcję, jeśli zwrócona wartość jest nieważna (std::function<"return value" ("parameters">
). Muszę to zrobić w czasie kompilacji i po prostu nie mogę sprawić, żeby działało tak, jak powinno. Oto niektóre kodu testu dla wyjaśnienia:Specjalizacja typu w czasie kompilacji
#include <iostream>
#include <type_traits>
template <typename T> class Test { };
template <typename Ret, typename... Args>
class Test<Ret (Args...)>
{
public:
Ret operator()(Args...)
{
if(std::is_void<Ret>::value)
{
// Do something...
}
else /* Not a void function */
{
Ret returnVal;
return returnVal;
}
}
};
int main(int argc, char * argv[])
{
Test<void (char)> test;
test('k');
}
Jak można wyraźnie zobaczyć, jeśli kompilator nie usunąć „innego” oddział w powyższym teście, mój kod będzie próbował utworzyć wartość void (tj void returnVal;
) . Problemem jest to, że kompilator nie usunąć gałąź więc skończyć z błędu kompilatora:
./test.cpp: In instantiation of ‘Ret Test::operator()(Args ...) [with Ret = void; Args = {char}]’: ./test.cpp:27:10: required from here ./test.cpp:18:8: error: variable or field ‘returnVal’ declared void ./test.cpp:19:11: error: return-statement with a value, in function returning 'void' [-fpermissive]
Jeden normalnie używać std::enable_if
połączeniu z std::is_void
, problemem jest to, że nie chcą się specjalizować na szablon funkcji, ale w szablonie klasy.
template <typename Ret, typename... Args>
class Test<Ret (Args...)>
{
public:
typename std::enable_if<!std::is_void<Ret>::value, Ret>::type
Ret operator()(Args...)
{
Ret returnVal;
return returnVal;
}
typename std::enable_if<std::is_void<Ret>::value, Ret>::type
Ret operator()(Args...)
{
// It's a void function
// ...
}
};
Jeśli używam powyższy kod zamiast skończyć z jeszcze większej liczby błędów i bez rozwiązania
./test.cpp:11:2: error: expected ‘;’ at end of member declaration
./test.cpp:11:2: error: declaration of ‘typename std::enable_if<(! std::is_void<_Tp>::value), Ret>::type Test<Ret(Args ...)>::Ret’
./test.cpp:6:11: error: shadows template parm ‘class Ret’
./test.cpp:11:24: error: ISO C++ forbids declaration of ‘operator()’ with no type [-fpermissive]
./test.cpp:18:2: error: expected ‘;’ at end of member declaration
./test.cpp:18:2: error: declaration of ‘typename std::enable_if<std::is_void<_Tp>::value, Ret>::type Test<Ret(Args ...)>::Ret’
./test.cpp:6:11: error: shadows template parm ‘class Ret’
./test.cpp:18:24: error: ISO C++ forbids declaration of ‘operator()’ with no type [-fpermissive]
./test.cpp:18:6: error: ‘int Test<Ret(Args ...)>::operator()(Args ...)’ cannot be overloaded
./test.cpp:11:6: error: with ‘int Test<Ret(Args ...)>::operator()(Args ...)’
./test.cpp: In member function ‘int Test<Ret(Args ...)>::operator()(Args ...)’:
./test.cpp:22:2: warning: no return statement in function returning non-void [-Wreturn-type]
./test.cpp: In instantiation of ‘int Test<Ret(Args ...)>::operator()(Args ...) [with Ret = void; Args = {char}]’:
./test.cpp:28:10: required from here
./test.cpp:13:7: error: variable or field ‘returnVal’ declared void
./test.cpp: In member function ‘int Test<Ret(Args ...)>::operator()(Args ...) [with Ret = void; Args = {char}]’:
./test.cpp:15:2: warning: control reaches end of non-void function [-Wreturn-type]
Przepraszam, jeśli jestem po prostu głupi, a odpowiedź jest oczywista. Jestem całkiem nowy w szablonach i nie mogłem znaleźć odpowiadającej odpowiedzi w żadnym z pozostałych wątków/pytań.
Istnieje propozycja włączenia "statycznego jeśli", który zrobiłby dokładnie to, co chcesz w swoim pierwszym przykładzie. – mfontanini
@mfontanini: Niezależnie od tego, czy któraś z dwóch propozycji faktycznie się rozwija, to inna sprawa. Mają niedociągnięcia, a niektóre z nich stają się o wiele bardziej skomplikowane, niż wyglądają, jeśli zaczniesz używać SFINAE z szablonami, które mają statyczny-jeśli jest –
"Problem polega na tym, że kompilator nie usuwa gałęzi, więc kończę z błędem kompilatora" = > Niestety, kompilator * jest wymagany, aby nie *, co (przyznaję) jest nieco denerwujące. –