Ich weiß, dass diese Frage Jahre alt ist, aber ich denke, dass es für Leute wie mich nützlich wäre, eine vollständigere, aktualisierte Antwort zu haben, die auch für const
überladene Methoden wie std::vector<>::begin
.
Auf der Grundlage dieser respuesta und dass respuesta auf meine Folgefrage, hier eine ausführlichere Antwort. Beachten Sie, dass dies nur mit C++11 und höher funktionieren wird.
#include <iostream>
#include <vector>
class EmptyClass{};
template <typename T>
class has_begin
{
private:
has_begin() = delete;
struct one { char x[1]; };
struct two { char x[2]; };
template <typename C> static one test( decltype(void(std::declval<C &>().begin())) * ) ;
template <typename C> static two test(...);
public:
static constexpr bool value = sizeof(test<T>(0)) == sizeof(one);
};
int main(int argc, char *argv[])
{
std::cout << std::boolalpha;
std::cout << "vector<int>::begin() exists: " << has_begin<std::vector<int>>::value << std::endl;
std::cout << "EmptyClass::begin() exists: " << has_begin<EmptyClass>::value << std::endl;
return 0;
}
Oder die kürzere Version:
#include <iostream>
#include <vector>
class EmptyClass{};
template <typename T, typename = void>
struct has_begin : std::false_type {};
template <typename T>
struct has_begin<T, decltype(void(std::declval<T &>().begin()))> : std::true_type {};
int main(int argc, char *argv[])
{
std::cout << std::boolalpha;
std::cout << "vector<int>::begin() exists: " << has_begin<std::vector<int>>::value << std::endl;
std::cout << "EmptyClass exists: " << has_begin<EmptyClass>::value << std::endl;
}
Beachten Sie, dass hier ein kompletter Musteraufruf vorgelegt werden muss. Das bedeutet, dass wir, wenn wir auf den resize
Existenz der Methode, dann hätten wir die resize(0)
.
Tiefe magische Erklärung :
In der ersten veröffentlichten Antwort auf diese Frage wurde test( decltype(&C::helloworld) )
Dies ist jedoch problematisch, wenn die zu prüfende Methode aufgrund von Überladung mehrdeutig ist, so dass der Ersetzungsversuch fehlschlägt.
Um diese Zweideutigkeit zu lösen, verwenden wir eine void-Anweisung, die beliebige Parameter annehmen kann, da sie immer in eine noop
und somit ist die Zweideutigkeit aufgehoben und der Aufruf ist gültig, solange die Methode existiert:
has_begin<T, decltype(void(std::declval<T &>().begin()))>
Hier ist die Reihenfolge der Ereignisse: Wir verwenden std::declval<T &>()
um einen abrufbaren Wert zu erzeugen, für den begin
kann dann aufgerufen werden. Danach wird der Wert von begin
als Parameter an eine void-Anweisung übergeben wird. Wir rufen dann den Typ dieses void-Ausdrucks mit dem eingebauten decltype
so dass es als Argument für einen Vorlagentyp verwendet werden kann. Wenn begin
nicht vorhanden ist, ist die Ersetzung ungültig und gemäß SFINAE wird stattdessen die andere Erklärung verwendet.