Given the following code. I want to detect any class that has an age
member field based on sfinae.
template <typename>
struct Void {
using type = void;
};
template <typename T, typename U = void>
struct HasAge : std::false_type {};
template <typename T>
struct HasAge<T, typename Void<decltype(declval<T>().age)>::type>
: std::true_type {};
struct Empty {};
struct PrivatePerson {
private:
int age;
};
struct PublicPerson {
public:
PublicPerson() = default;
int age;
};
template <typename T, typename = void>
struct HasPointer : std::false_type {};
template <typename T>
struct HasPointer<T, typename Void<typename T::pointer>::type>
: std::true_type {};
struct FancyPointer {
using pointer = int;
};
struct HazardPointer {
template <typename T>
struct pointer {};
};
int main() {
using namespace std;
cout << boolalpha;
cout << HasAge<Empty>::value << endl; // false
cout << HasAge<PrivatePerson>::value << endl; // false
cout << HasAge<PublicPerson>::value << endl; // false
cout << HasPointer<FancyPointer>::value << endl; // true
cout << HasPointer<HazardPointer>::value << endl;// false
}
The above code can't detect whether any class has an age
member field or not. It seems only the primary template is applied. But this strategy seems to work for typedef or using an alias inside a class. It could detect class defines a pointer
type or not. Could anyone tell me why age
detection failed? Thx.
In the posted code, this template:
template <typename T>
struct HasAge<T, typename Void<decltype(declval<T>().age)>::type>
// ^^^^^^^
: std::true_type {};
Is defined before a using namespace std;
statement, which is in main
.
Compiling with g++ -std=c++11 -Wall -Wextra -pedantic
, we get errors:
<source>:14:41: error: 'declval' was not declared in this scope; did you mean 'std::declval'?
14 | struct HasAge<T, typename Void<decltype(declval<T>().age)>::type>
| ^~~~~~~
| std::declval
Clang, with the same arguments, emits a warning1 instead:
<source>:14:41: warning: use of function template name with no prior declaration in
function call with explicit template arguments is a C++20 extension [-Wc++20-extensions]
struct HasAge<T, typename Void<decltype(declval<T>().age)>::type>
If we compile with g++
using -std=c++20 -Wall -Wextra -fpermissive
, we obtain a similar warning and the same (unexpected) result2 as OP's.
<source>:13:41: warning: there are no arguments to 'declval' that depend on a template parameter, so a declaration of 'declval' must be available [-fpermissive]
13 | struct HasAge<T, typename Void<decltype(declval<T>().age)>::type>
| ^~~~~~~~~~
To make this pre-C++20 detection idiom implementation work3, we just need to fully qualify std::declval
.
1) https://godbolt.org/z/dPc5aMEr8
2) https://godbolt.org/z/rP74hnKhs
3) https://godbolt.org/z/94fqcTEs7