I was reading this prehistoric metaprogam example to detect whether a class supports member find. (or any other member).
template<typename T>
class DetectFind
{
struct Fallback { int find; };
struct Derived : T, Fallback { };
template<typename U, U> struct Check;
typedef char Yes[1];
typedef char No[2];
template<typename U>
static No& func(Check<int Fallback::*, &U::find>*);
template<typename U>
static Yes& func(...);
public:
typedef DetectFind type;
enum { value = sizeof(func<Derived>(0)) == sizeof(Yes) };
};
int main()
{
std::cout << DetectFind<std::vector<int> >::value << std::endl;
std::cout<< DetectFind<std::set<int> >::value << std::endl;
}
Intuitively I do understand the aim behind this, but if someone would ask me to write same thing from scratch after 10 days, I maybe will fail this.
The reason for that is that I do not fully understand the syntactical and language stretch that is used here.
Can someone please explain what the following syntaxes mean?
Check<int Fallback::*, &U::find>*
(I know its trying to benefit from SFIANE here, but how is this detecting the existence of find, I beleive this is linked to the second question as well)template<typename U, U> struct Check;
The program outputs 0 1 as predicted;
There is no difference. typename and class are interchangeable in the declaration of a type template parameter.
" typename " is a keyword in the C++ programming language used when writing templates. It is used for specifying that a dependent name in a template definition or declaration is a type.
There is no difference between using <typename T> OR <class T> ; i.e. it is a convention used by C++ programmers.
A template parameter is a special kind of parameter that can be used to pass a type as argument: just like regular function parameters can be used to pass values to a function, template parameters allow to pass also types to a function.
The template and typename keywords are routinely used to define templates. This is not the topic of this page as we assume that the reader is aware of this (otherwise consult a C++ book). The following example should illustrate this use of the template keyword.
In Visual Studio 2017 and later, and in /std:c++17 mode or later, the compiler deduces the type of a non-type template argument that's declared with auto: A template can be a template parameter. In this example, MyClass2 has two template parameters: a typename parameter T and a template parameter Arr:
The template and typename keywords are routinely used to define templates. This is not the topic of this page as we assume that the reader is aware of this (otherwise consult a C++ book). The following example should illustrate this use of the template keyword. template <typename T>. bool isPositive(T x)
A template is a construct that generates an ordinary type or function at compile time based on arguments the user supplies for the template parameters. For example, you can define a function template like this:
template<typename U, U>
means there are two template arguments: an arbitrary type U, and an unnamed non-type template argument (eg. a value argument) whose type is U. As an example, an int
scenario would be ClassName<int,42>
.
In your example, the type U
is a pointer to int
member, and the value is the address of an int
member.
First, let us consider the struct Derived
. Since it derives from Fallback
it certainly contains a int field find
, and possibly a member function find
, whose existence is what you want to check.
As noted in the answer above, in the declaration of the struct Check
, the first template parameter is as type, and the second is a non-type parameter, of the type given by the first parameter.
Given that, let us examine the two overloads of func
. The first overload takes a pointer to a Check
struct, whose first template parameters is a type equal to pointer-to-int member of Fallback
(int Fallback::*
). The second template parameter is then interpreted as a pointer-to-int member with value is &U::find
.
Given U = Derived
, if T
contains a find
member function, this second parameter of Check
is ambiguous, as it could also refer to the int find
inherited by Fallback
. By SFINAE, this overload of func
will be then discarded.
The second func
overload is always well defined. But if the first overload is not discarded, the second one is less specialized, so the the compiler will choose the first overload.
In summary: if U
in template <typename U> func
contains a member function find
, the compiler chooses the second overload of func
. If the member function find
is absent, the compiler chooses the first overload.
Finally, the value of DetectFind
is determined by the size of the returning type of the chosen func
, which depending on the overload is a char array of size 1 or 2. From there, you get which overload of func
is chosen, and from the discussion above, whether T
has a member function find
or not.
Let's firstly cover the declaration of struct Check
;
template<typename U,U> struct Check
means a that template arguments are
U
U
, which doesn't have name.Check<int Fallback::*, &U::find>*
is a little bit more confusing.
The first template argument is a pointer to member., in this case it stands for a pointer to an member of class Fallback
of type int
, which would have allowed us to write something like this.
Fallback obj{10};
int Fallback::* find_ptr = &Fallback::find;
std::cout << obj.*find << std::endl; //prints 10, note: ".*" is a separate operator
The second argument of the template is the address of the member of class U
, as the initial declaration of struct Check
suggested.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With