Consider classes with member variables, like these:
struct A {
int a;
char b;
};
struct B {
double c;
bool d;
};
Is it possible to declare a template class that accepts as its template
argument a pointer-to-member-object to any one of the members declared
in the above classes?
A class that accepts a generic pointer-to-member-object could be
declared and used as follows:
template<typename Class, typename Type, Type (Class::*Member)>
struct Magic
{ };
// Usage:
typedef Magic<A, int, &A::a> MagicWithA_a;
Unfortunately it is quite cumbersome having to pass in the Class and
Type template arguments every time to make the final pointer work.
Is there any way to have these arguments deduced by partial specialization,
for example? That is, how can the Magic class be declared to make the
below definitions work?
typedef Magic<&B::c> MagicWithB_c;
typedef Magic<&A::b> MagicWithA_b;
With C++17 you can use auto non-type template parameter:
template<auto p_member>
struct Magic
{ };
Before C++17 only the longer variant that you've implemented would work.
You can shorten it somewhat with specializations, yes. And if you don't mind resorting to macros you can have almost the syntax you want with c++11. First, a primary template specialization:
template<typename T, T pm>
struct MagicImpl; // Undefined for most types
Then a partial specialization for pointers to members, where we can freely add the parameters we wish to be deduced:
template<class Class, typename Type>
struct MagicImpl<Type Class::*, Type (Class::*Member)> {
};
And finally, we need to employ decltype to get the pointer to member type out of the pointer-to-member expression, which we hide behind the aforementioned macro:
#define MAGIC(...) MagicImpl<decltype(__VA_ARGS__), __VA_ARGS__>
And you can use it as follows:
typedef MAGIC(&B::c) MagicWithB_c;
typedef MAGIC(&A::b) MagicWithA_b;
Related
While answering one of SO questions I came across code like this:
template<class T, class... Ts> void foo();
template <class T, class T::value_type>
void foo() { }
It was presented as code specializing foo template function, which is not right, but that is not my issue here. I would like to know why does compiler allow construcion like this: class T::value_type in the template parameter. I mean, it is pretty clearly wrong, I cannot come up with any situation that scope operator may be part of the argument name (either template or function). And so I have two qestions:
Does standard allow it or is it an overlook in compilers?
If standard allows it, why is it so? Are there any use cases?
As mentioned in the comments, it's an elaborated type specifier. Best explained with an example:
int main() {
struct foo {}; // ok
int foo = 0; // ok
int test = foo; // ok, refers to variable 'foo'
foo a; // error, 'foo' refers to variable
struct foo b; // ok, 'struct' means that name lookup searches for classes only
}
In essence, you can think of them (struct/class, enum) as a more restricted typename, as they only allow classes or enums respectively. Also do note that typename is allowed in your original example!
template<class T, class... Ts> void foo();
template <class T, typename T::value_type> // Ok, value_type needs to be a type
// ^^^^^^^^^^^^^^^^^^^^^^^ it's a non-type template parameter
void foo() { }
It is needed when you have a type and a variable with the same name, or to specify what the thing is when you have a dependent name (i.e. in class T::value_type, value_type is a class, without the class before, it would have been a value. Normally, a typename is used.)
I cannot come up with any situation that scope operator may be part of the argument name
Your only thinking of type template parameters here; non-type template parameters can very well use a scope operator to name the type.
what template <class = typename T::type> means? Can you refer me to some blog, specification describing this?
The question originaly came from explanation of sfinae on cpp reference
template <typename A>
struct B { typedef typename A::type type; };
template <
class T,
class = typename T::type, // SFINAE failure if T has no member type
class U = typename B<T>::type // hard error if T has no member type
// (guaranteed to not occur as of C++14)
> void foo (int);
First, I'll explain typename T::type. This is simply the access of a member type. Here's an example of accessing a member type:
struct foo {
using bar = int;
};
int main() {
foo::bar var{};
// var has type int
}
So why the typename? It simply means we want to access a type. Since we are in a template and T is an unknown type, and foo::bar could also mean accessing a static variable. To disambiguate, we denote that we effectively want to access a type by explicitly typing typename.
Okay, now what does the class = means?
The class = means the same thing as typename =. When declaring template type parameters, we introduce then using class or typename:
template<typename A, typename B>
struct baz {};
But as any parameters in C++, the name is optional. I could have wrote this and the following is completely equivalent:
template<typename, typename>
struct baz {};
Also, you know in function parameters, we can specify default values? Like that:
void func(int a, int b = 42);
int main () {
func(10); // parameter b value is 42
// We are using default value
}
We can also omit parameter names:
void func(int, int = 42);
Just like function parameters, template parameters can have it's name omitted, and can have a default value.
template<typename, typename = float>
struct baz {};
baz<int> b; // second parameter is float
Putting it all together
Now we have this declaration:
template <
class T,
class = typename T::type, // SFINAE failure if T has no member type
class U = typename B<T>::type // hard error if T has no member type
// (guaranteed to not occur as of C++14)
> void foo (int);
Here we declare a function that takes an int as parameter, and has three template parameter.
The fist parameter is a simple named parameter. The name is T and it's a type template parameter. The second is also a type parameters, but it has no name. However, it has a default value of T::type, which is a member type of T. We are explicitly telling the compiler that T::type must be a member type of T by specifying typename. The third parameter is similar to the second.
This is where SFINAE kicks in: when a default parameter is used, but T::type as as a member type don't exist, how can you assign the second template parameter to that? We can't. If T::type don't exists, we cannot assign the second template parameter. But instead of making it an error, the compiler will simply try another function, because there is a chance another function would be callable.
This is quite similar to simple overloading. You have the f function. It takes a float parameter, an another overload that takes a std::string. Imagine you call f(9.4f). Does the compiler choke because a std::string is not constructible from a float? No! The compiler ain't stupid. It will try another overload, and will find the float version and call it. In SFINAE a similar analogy can be made. The compiler won't stop because there's some overload somewhere that needs an undefined type in a template parameter. It will try another overload.
template <bool condition>
struct when;
template <typename It, typename = void>
struct at_impl : at_impl<It, when<true>> { };
struct at_t {
template <typename Xs, typename N>
constexpr decltype(auto) operator()(Xs&& xs, N const& n) const;
};
constexpr at_t at{};
int main()
{
}
How could this program compiled? How can a struct inherits itself?!
I don't know what's going on here. Is this a new feture in c++?
How can a struct inherits itself?
No. A struct can not inherit itself.
A struct that is an instance of a struct template can inherit another instance of the same template, as long as the parent is instantiated with different template arguments †.
at_impl is not a struct; it is a template.at_impl<It, when<true>> is an instance of the template, and it is a struct.
Note that the parent struct must be complete at the time the derived struct is instantiated. This is possible if the parent is a specialization. Your example shows neither the definition of at_impl<It, when<true>> specialization, nor any instantiation of at_impl.
This is analogous to functions. A function can call itself recursively, but the function arguments must change. If a function calls itself with the same arguments, then the recursion can never terminate. A function with no arguments can not be recursive ††.
Now, a template has arguments and is analogous to a function with parameters. A type has no template arguments and is analogous to a function with no parameters. Instantiating a template results in a type and is analogous to a function call.
† This is a necessary condition for the termination of the recursion, but not sufficient.
†† Unless the function depends on global state, but from abstract point of view, we must consider global state as an implicit argument in this context.
at_impl is a struct/class template, from which a struct/class is generated.
Different template arguments to at_impl generate different structs. Inheriting from a different struct/class is allowed.
Is it possible to define a template which takes a single pointer parameter and extracts the type pointed to?
extern int three = 3;
typename examine<&three>::pointed_type // int
There's already std::remove_pointer<T>::type in <type_traits> (C++11) that you can use.
Yes, you would use partial specialization to achieve this, similar to the following:
template<typename T>
struct examine {
typedef T pointed_type;
};
template<typename T>
struct examine<T*> {
typedef T pointed_type;
};
To refer to your comment about usage of non-type template parameters, consider the following:
template<typename T, T* p>
struct foo { };
extern int three = 3;
foo<decltype(three), &three> bar;
As you can see, foo can indeed take a template parameter of &x, but to do so we first need to give it a type parameter (or restrict it to int*, which does not help at all). If this is not done, the name T is not that of any type when the template parameter p is defined.
There is no way at all to perform any kind of automatic deduction of T, since that would require the template arguments to be defined in the other order.
The closest you can get without using decltype is by defining a function template that gets a non-template argument that will cause the template type parameter to be deduced like so
template<typename T>
void deduce_argument_type(T const&)
{
// here, the type T is (close to) that of your argument
}
deduce_argument_type(&three); // uses deduce_argument_type<int*>
Again, you cannot use this to get around the restriction on defining T before passing &three, since template parameter deduction only deduces any types to the right of those template parameters you passed.
Suppose you have template method that takes a pointer to a method of any type. Inside that template, is there a way to turn the template argument, which is a method pointer type (void(A::*)() for example), into a function pointer type with the same return type and parameters, but without the class information (void(*)())?
I looked at the <type_traits> header but didn't find anything that would help.
A simple way in C++11 is partial specialization with a variadic template:
template<class PMF> struct pmf_to_pf;
template<class R, class C, class... Args>
struct pmf_to_pf<R (C::*)(Args...)>{
using type = R(*)(Args...); // I like using aliases
};
Note that you need a seperate partial specialization for const member-functions (and theoretically for ref-qualified ones aswell):
template<class R, class C, class... Args>
struct pmf_to_pf<R (C::*)(Args...) const>{
using type = R(*)(Args...);
};
With cv-qualifiers ({nil,const}, {nil,volatile}) and ref-qualifiers ({nil, &, &&}), you have a total of 2*2*3 == 12 partial specializations to provide†. You normally can leave out the volatile qualification ones, dropping the total down to "just" 6. Since most compilers don't support function-ref-qualifiers, you only need 2 really, but be aware of the rest.
For C++03 (aka compilers without variadic templates), you can use the following non-variadic form:
template<class PMF> struct pmf_to_pf;
template<class Sig, class C>
struct pmf_to_pf<Sig C::*>{
typedef Sig* type; // 'Sig' is 'R(Args...)' and 'Sig*' is 'R(*)(Args...)'
};
Although this doesn't quite work with cv-qualifiers, since I think C++03 didn't allow cv-qualified signatures (e.g. R(Args...) const) on which you could partially specialize to remove the const.
† The {...} denote a set of a certain qualifier, with nil representing the empty set. Examples:
void f() const& would be {const}, {nil}, {&}
void f() && would be {nil}, {nil}, {&&}
And so on.