Hi,
This in not a well-formed proposal but
just my thoughts about the subject feature. May be something similar is already planned but I did not find it in C++14 features list.
So now, especially after introducing “N2439 - Extending move semantics to *this”, (*this) can be matched to differently qualified type:
class A {
void Method(); // 1. *this is "A&"
void Method() const; // 2. *this is "const A&"
void Method() volatile; // 3. *this is "volatile A&"
void Method() const volatile; // 4. *this is "const volatile A&"
void Method() &&; //5. matched to "A &&" but in scope of method implementation *this is "A&" since it has name
void Method() const &&; //6. This one and similar CV+rvalue_ref, may be not so meaningful in most cases but still worth to mention. *this is "const A&", probably always matches "A&&".
};
Currently there is no way to create a method template, having type of “*this” as template parameter. In some cases you might want to have generic implementation for a method and properly forward “*this” to some other function. The number of combinations for *this types reminds me the situation with possible qualifiers for arguments in templates before perfect forwarding pattern was introduced. Fortunately, now we have only one problematic argument with dozen of choices (which were doubled after N2439).
Small real world example of the applicable situation. Let's say I have a class which encapsulates some value. The value has dynamic type, and may have underlying type which requires heap allocation (e.g. string) so it is perfect for moving. I have method Get<TargetType>() which returns the value. The trick with the Get() method is that for “rvalue *this” we can move the stored value to the result returned to the caller (and this is very cool language feature indeed).
class Value {
template <typename T>
T
Get() const &
{
// return a copy of the stored value
}
template <typename T>
T
Get() &&
{
// move the stored value to the returned result
}
// And here we might want to have some aliases
template <typename T>
operator T() const &
{
return Get<T>();
}
template <typename T>
operator T() &&
{
return std::move(*this).Get<T>();
}
template <typename T>
T
GetWithStuff() const &
{
return Get<T>() + Stuff<T>();
}
template <typename T>
T
GetWithStuff() &&
{
return std::move(*this).Get<T>() + Stuff<T>();
}
};
The problem is that Get() method has some aliases (it might be another method with just different name or some generalized additional logic, or, for example, type casting operator). Did you also noticed copy-pasting which naturally seems must be avoided? What I basically might want from the language, is the ability to perfectly forward “*this” to some functions. Possible syntax (just the first idea):
template <this ThisType, typename T>
T
GetWithStuff() ThisType && //roughly equivalent to "T&&" in arguments when using arguments forwarding
{
return std::forward<ThisType>(*this).Get<T>() + Stuff<T>();
}
I think “this” keyword can be used in template parameters which is very natural. Also I feel that all rules applicable to type deduction of argument in “T &&” form (in perfect forwarding pattern context), as well as all rules for argument types and templates there, should be the same for “this” argument which is just written next to parenthesis. Thus such construction
template <this ThisType, typename T>
T
GetWithStuff() const ThisType &
{
return std::forward<ThisType>(*this).Get<T>() + Stuff<T>();
}
will not have much sense (ThisType will be just “Value”) but still should work to make things consistent.
May be having template parameter name in “*this” specification part is redundant:
template <this ThisType, typename T>
T
GetWithStuff() const & //presence of "this" in template parameters automatically applies it there
{
return std::forward<ThisType>(*this).Get<T>() + Stuff<T>();
}
Obviously “this” can be used in template parameters for non-static class methods only.
IMHO such feature would be a nice addition for making the language even more beautiful, and it should not be a problem to implement in compilers since “this” argument already should be implicitly present (with type information) in most places, which are subject for the changes.
Best regards,
Artyom
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.
I'm not sure if it'd be better or not to make it a template (I haven't put very much thought into it yet), but I just wanted to mention this option.
Also, there needs to be some way to specify you only want to define all the & versions but not the && versions, or only the const versions. Something like:
void whatever() T & { ... }
void whatever() T const { ... }
This would make repeating the template paramaeter name after the argument list non-redundant in your template idea.
I definitely sure that it must be a template. Basically we have a function template which has "this" implicit argument type as a template parameter, i.e. depending on the provided "this" argument type different function code is instantiated - this is true for this case and it is how templates work. Also examples below in this message will show, how it is close to usual templatized argument.I'm not sure if it'd be better or not to make it a template (I haven't put very much thought into it yet), but I just wanted to mention this option.
Also, there needs to be some way to specify you only want to define all the & versions but not the && versions, or only the const versions. Something like:
void whatever() T & { ... }
void whatever() T const { ... }
This would make repeating the template paramaeter name after the argument list non-redundant in your template idea.
(snip)
So what I want to say, is that CV-qualifiers and lvalue/rvalue reference after parenthesis is just a syntactic sugar for the case if "this" argument was explicit and was written inside parenthesis and with the name. And still seems that the name is not needed there because we always know what and where it is. Correct me if I'm wrong.
struct s {
int x;
template< typename self >
friend
typename std::enable_if< std::is_base_of< s, typename std::decay< self >::type >::value,
std::decltype( std::declval< self >().x ) >::type
get_x( self && o )
{ return std::forward< self >( o ).x; }
};
Standard disclaimer: accessor functions are evil. C++ has a perfectly good model for accessing members; do not roll your own unless you legitimately have a custom object model.
You can't get the actual type of the argument which initialized *this; as noted only templates can represent simultaneous types and otherwise you have all the combinations of cv-qualification and value category.
As you mentioned, const rvalue isn't actually used for anything, so the const overload need not be ref-qualified.
--
Standard disclaimer: accessor functions are evil. C++ has a perfectly good model for accessing members; do not roll your own unless you legitimately have a custom object model.Probably you a bit misunderstood the idea. It by no means related to accessor functions.
If the friend function is an unpalatable interface, the member overloads to call it will always be only one line each.
struct S {And here is a replacement with the new feature:
std::string x;
//replace (decltype(std::declval<Self>().x) &) to some more advanced
//construction, so that lvalue and rvalue reference would properly created.
//I do not even want to do it.
template<typename Self>
friend
typename std::enable_if<std::is_base_of<S, typename std::decay<Self>::type>::value,
decltype(std::declval<Self>().x) &>::type
_get_x(Self &&self)
{ return std::forward<Self>(self).x; }
std::string &
Get_x() &
{
return _get_x(*this);
}
const std::string &
Get_x() const &
{
return _get_x(*this);
}
volatile std::string &
Get_x() const &
{
return _get_x(*this);
}
const volatile std::string &
Get_x() const volatile &
{
return _get_x(*this);
}
std::string &&
Get_x() &&
{
return _get_x(std::move(*this));
}
std::string const &&
Get_x() const &&
{
return _get_x(std::move(*this));
}
std::string volatile &&
Get_x() volatile &&
{
return _get_x(std::move(*this));
}
std::string const volatile &&
Get_x() const volatile &&
{
return _get_x(std::move(*this));
}
};
struct S {The templates there are still long enough, but the example is less real than I previously mentioned. I also repeat the other my thought - in general, the idea is that now we have full set of qualifiers for "*this", just like for any other argument, but lack possibility to make its type templatized, like any other argument. In my opinion, it would be very logically, and now it looks like unfinished solution. So this is ideological question.
std::string x;
template <this Self>
auto
Get_x() && -> decltype(std::forward<decltype(std::declval<Self>().x)>(std::declval<Self>().x))
{
return std::forward<decltype(std::declval<Self>().x)>(std::declval<Self>().x);
}
};
--
---
You received this message because you are subscribed to a topic in the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this topic, visit https://groups.google.com/a/isocpp.org/d/topic/std-proposals/xMlthAq8DWk/unsubscribe.
To unsubscribe from this group and all its topics, send an email to std-proposal...@isocpp.org.
struct S {
std::string x;
//replace (decltype(std::declval<Self>().x) &) to some more advanced
//construction, so that lvalue and rvalue reference would properly created.
template<typename Self>
friend
decltype( auto )
_get_x(Self &&self)
{ return std::forward<Self>(self).x; }
std::string &
Get_x() &
{
return _get_x(*this);
}
const std::string &
Get_x() const
{
return _get_x(*this);
}
std::string &&
Get_x() &&
{
return _get_x(std::move(*this));
}
};