Proposal for overriding of type deduction

275 views
Skip to first unread message

DeadMG

unread,
Nov 22, 2012, 6:04:14 PM11/22/12
to std-pr...@isocpp.org
The problem is fairly simple: It's too difficult to implement proxy objects in C++ right now.

In C++11, I feel that, with type deduction, we should move away from concrete types and move to deal with only the interfaces they offer.

The primary difficulty here comes when we deal with auto and decltype. These functions, whilst great, break any attempts we make to offer an interface-based substitute. Especially consider the case of expression templates- currently, they do not really work, as you cannot do

auto a = b + c + d;

where the author of a has invoked expression templates to reduce complexity. Nor could you do 

std::vector<decltype(b + c)> vec;

Thus, I propose an "automatic" operator. The automatic operator is invoked whenever type deduction is used, including decltype or auto, or whenever a requested member is not available. In addition, when overload resolution or template specialization is considered, an object which has an automatic operator is considered as the return type of that automatic operator, not as it's actual type, unless the actual type is specified directly and unambiguously with no intermediate conversions (except reference binding, cv qualifiers). The sample syntax I am showing is auto operator T(), which I consider to be the best choice, but it's a bit of a bikeshed issue as it's only used once at the point of definition and thus not especially significant.

For now, I think it would be simplest to define that each type may only have one automatic operator, except that the operator may be overloaded by cv-qualifier or ref-qualifer- that is, any expression e involving any type T may automatically convert to one, and only one, type.

For example, consider an expression template. Now, in our proxy objects, we can define an automatic conversion.

class proxy {
    auto operator real_type() && { return evaluate(); }
};

This would permit us to write

std::vector<decltype(b + c)> vec;

where vec's template argument is actually real_type, not proxy. In addition, we can perform operations like (b + c).size(); without having to explicitly forward each and every member in a non-generic and unmaintainable fashion. Finally, we can introduce new types which are indistinguishable from previous types, enabling improvements to areas of the Standard not previously accessible. For example, consider basic_string. Right now, we could not change that specification to support expression templates, which offer improved complexity even over move semantics, because that would break code which relied on type deduction or template specialization for the return type of operator+. However, if the wording were changed to say that the return value of operator+ offers the same interface as basic_string, it would now be possible for implementations to replace it with an optimized version without breaking existing code, by having an automatic conversion. In fact, such a wording change may not even be necessary, since the change would not be observable and thus falls under the as-if rule.

It would also permit relaxed requirements in some areas of the Standard. For example, consider operator[] for map and unordered_map. By returning a value which has an automatic conversion to value&, we can effectively overload for read or write separately- and there have been many requests for, for example, an operator[]=. This proposal would effectively solve that problem, as well as, say, operator*= (for *it = x) for iterators. The difference between what can be accomplished now, and an implementation using an automatic conversion, is that it would be impossible to write a generic proxy which also supported members- such as it->x.

struct proxy {
    auto operator value&() { 
        // read: if T is not default-constructible, throw if not found.
    }
    template<typename T> value& operator=(T&& arg) { 
        // write: don't require default-constructible 
    }
};

Now, assuming a proxy operator[], we can write

map[key].f();

and

map[key] = val;

without having to require default-constructibility, and potentially improving performance by avoiding default-construction and then assignment.

Finally, we also move a step closer to properties and other similar mechanisms, since a value of one type can transparently impersonate another.

sghe...@gmail.com

unread,
Nov 22, 2012, 6:16:36 PM11/22/12
to std-pr...@isocpp.org
Fairly interesting. However, this is going to lead to something akin to the problems with overriding operator&

With operator& overridden, library writers are going to have to use `std::addressof<>()` in their code to avoid unintentionally executing unknown user logic or even just to avoid invoking ADL on the template (and all types named in it's template arguments etc; the compilation time implications of this are a bit off topic, but shallow instantiation can cause code to fail to compile where it would not need to fail). 

So, how would you propose people (presumably library writers) would _override_ this mechanism and get the _actual_ decltype of an 'expression template' which was auto-type-deduction-enabled?

DeadMG

unread,
Nov 22, 2012, 6:31:42 PM11/22/12
to std-pr...@isocpp.org, sghe...@gmail.com
You can't. That's the entire purpose of the feature. If you are a library writer without previous explicit knowledge of that type, then it is completely non-observable whether you are dealing with a proxy or a non-proxy- in fact, you would never, ever deal with the proxy object, either implicitly or explicitly, because if a proxy was passed to your function, it would be automatically converted to a real_type.

sghe...@gmail.com

unread,
Nov 22, 2012, 7:22:06 PM11/22/12
to std-pr...@isocpp.org, sghe...@gmail.com
On Friday, November 23, 2012 12:31:43 AM UTC+1, DeadMG wrote:
You can't. That's the entire purpose of the feature. If you are a library writer without previous explicit knowledge of that type, then it is completely non-observable whether you are dealing with a proxy or a non-proxy- in fact, you would never, ever deal with the proxy object, either implicitly or explicitly, because if a proxy was passed to your function, it would be automatically converted to a real_type.

Hmm. That might be a sound strategy. Certainly looks the simplest.

I think I had a brainfart relating to ADL for user-definef conversion operators in general. Conversion ops can't be 'friend' declared, so in fact the compiler wouldn't need to shallow instantiate any ADL associated type namespaces to resolve any 'auto' conversion operator.

Let's see what others have to say about this proposal


DeadMG

unread,
Nov 22, 2012, 7:25:10 PM11/22/12
to std-pr...@isocpp.org, sghe...@gmail.com
I think also, what's important to note is that overloading operator& gives an unusual interface. The whole purpose of this proposal allows unusual implementations to conform to usual interfaces. It should significantly reduce headache, not increase it.

Nicol Bolas

unread,
Nov 22, 2012, 8:16:12 PM11/22/12
to std-pr...@isocpp.org


On Thursday, November 22, 2012 3:04:15 PM UTC-8, DeadMG wrote:
The problem is fairly simple: It's too difficult to implement proxy objects in C++ right now.

In C++11, I feel that, with type deduction, we should move away from concrete types and move to deal with only the interfaces they offer.

The primary difficulty here comes when we deal with auto and decltype. These functions, whilst great, break any attempts we make to offer an interface-based substitute. Especially consider the case of expression templates- currently, they do not really work, as you cannot do

auto a = b + c + d;

where the author of a has invoked expression templates to reduce complexity. Nor could you do 

std::vector<decltype(b + c)> vec;

Thus, I propose an "automatic" operator. The automatic operator is invoked whenever type deduction is used, including decltype or auto, or whenever a requested member is not available. In addition, when overload resolution or template specialization is considered, an object which has an automatic operator is considered as the return type of that automatic operator, not as it's actual type, unless the actual type is specified directly and unambiguously with no intermediate conversions (except reference binding, cv qualifiers). The sample syntax I am showing is auto operator T(), which I consider to be the best choice, but it's a bit of a bikeshed issue as it's only used once at the point of definition and thus not especially significant.

For now, I think it would be simplest to define that each type may only have one automatic operator, except that the operator may be overloaded by cv-qualifier or ref-qualifer- that is, any expression e involving any type T may automatically convert to one, and only one, type.

For example, consider an expression template. Now, in our proxy objects, we can define an automatic conversion.

class proxy {
    auto operator real_type() && { return evaluate(); }
};

Why not `operator auto()`? Someone out there might actually have a `real_type`, so we can't keyword it. Plus, this is what it is, right? It's a conversion operator that is called when `proxy` is automatically type-deduced. It makes sense to use `auto` in that context.
 

Nicol Bolas

unread,
Nov 22, 2012, 8:17:09 PM11/22/12
to std-pr...@isocpp.org


On Thursday, November 22, 2012 5:16:12 PM UTC-8, Nicol Bolas wrote:


On Thursday, November 22, 2012 3:04:15 PM UTC-8, DeadMG wrote:
The problem is fairly simple: It's too difficult to implement proxy objects in C++ right now.

In C++11, I feel that, with type deduction, we should move away from concrete types and move to deal with only the interfaces they offer.

The primary difficulty here comes when we deal with auto and decltype. These functions, whilst great, break any attempts we make to offer an interface-based substitute. Especially consider the case of expression templates- currently, they do not really work, as you cannot do

auto a = b + c + d;

where the author of a has invoked expression templates to reduce complexity. Nor could you do 

std::vector<decltype(b + c)> vec;

Thus, I propose an "automatic" operator. The automatic operator is invoked whenever type deduction is used, including decltype or auto, or whenever a requested member is not available. In addition, when overload resolution or template specialization is considered, an object which has an automatic operator is considered as the return type of that automatic operator, not as it's actual type, unless the actual type is specified directly and unambiguously with no intermediate conversions (except reference binding, cv qualifiers). The sample syntax I am showing is auto operator T(), which I consider to be the best choice, but it's a bit of a bikeshed issue as it's only used once at the point of definition and thus not especially significant.

For now, I think it would be simplest to define that each type may only have one automatic operator, except that the operator may be overloaded by cv-qualifier or ref-qualifer- that is, any expression e involving any type T may automatically convert to one, and only one, type.

For example, consider an expression template. Now, in our proxy objects, we can define an automatic conversion.

class proxy {
    auto operator real_type() && { return evaluate(); }
};

Why not `operator auto()`? Someone out there might actually have a `real_type`, so we can't keyword it. Plus, this is what it is, right? It's a conversion operator that is called when `proxy` is automatically type-deduced. It makes sense to use `auto` in that context.

I forgot to mention that the return type would be the one that is the type that it would be deduced as.
 

Xeo

unread,
Nov 22, 2012, 8:21:52 PM11/22/12
to std-pr...@isocpp.org
On Friday, November 23, 2012 2:16:12 AM UTC+1, Nicol Bolas wrote

Why not `operator auto()`? Someone out there might actually have a `real_type`, so we can't keyword it. Plus, this is what it is, right? It's a conversion operator that is called when `proxy` is automatically type-deduced. It makes sense to use `auto` in that context.

I don't think that `real_type` was meant as anything other than a placeholder for some type.

DeadMG

unread,
Nov 22, 2012, 8:27:06 PM11/22/12
to std-pr...@isocpp.org
Yeah. real_type is not a keyword or anything like that, it's any type or type-expression-thingy like std::remove_pointer<T>::type (I forget the Standardese).

Why not `operator auto()`?

Because that doesn't tell the compiler what the intended conversion target is, only that you wish to convert. That's not enough information to perform the conversion. 

Nicol Bolas

unread,
Nov 22, 2012, 8:57:06 PM11/22/12
to std-pr...@isocpp.org

The concern is that `auto operator <insert typename here>() {...}` looks very much like `auto operator +() {...}`, which mean two very different things. If we have auto-deduced return values, then `auto operator <something>` should mean automatically deducing the return value of an `operator <something>`.

Grammatically it should parse, since the `<something>` is a typename rather than a proper operator name. But from a user perspective, it looks similar yet behaves differently.

Also, my way lets you do this:

auto operator auto() {return DeduceTheTypeOfThis();}

Or, a more real example:

auto operator auto() {return (std::string)(*this);}

This would invoke the conversion constructor for `this`, or any implicit constructors for `std::string`. And I don't have to type the typename multiple times.

So if I have some expression type `Expr`, and I have some value-type, I can just do this:

auto operator auto() {return (value_type)(*this);}

I can put that conversion into either `value_type` or `Expr`, as I see fit. And again, I won't have to

Having it be `operator auto` means that it's an operator that's specifically invoked for type-deduction contexts. It also allows you to neatly duck issues like:

auto operator std::string() {...}
auto operator int() {...}

Because you can only declare `operator auto` once. Your way would require special language that says that you can only have an single conversion operator declared with `auto`.

I just think it's more clear having it be `operator auto` rather than `auto operator`.

DeadMG

unread,
Nov 22, 2012, 8:58:48 PM11/22/12
to std-pr...@isocpp.org
But, more importantly, you can only return deduced types. I don't personally have a problem with allowing automatic conversions to deduced types, but I do have a problem with not permitting non-deduced types.

Nicol Bolas

unread,
Nov 22, 2012, 9:05:22 PM11/22/12
to std-pr...@isocpp.org
On Thursday, November 22, 2012 5:58:48 PM UTC-8, DeadMG wrote:
But, more importantly, you can only return deduced types. I don't personally have a problem with allowing automatic conversions to deduced types, but I do have a problem with not permitting non-deduced types.

I'm not entirely sure how that applies to what I said.

DeadMG

unread,
Nov 22, 2012, 9:06:35 PM11/22/12
to std-pr...@isocpp.org
Your proposal details no means of specifying the return type if necessary.

DeadMG

unread,
Nov 22, 2012, 9:10:26 PM11/22/12
to std-pr...@isocpp.org
A friend of mine suggested that you might mean

T operator auto()

as in, a non-conversion operator. That would certainly be fine. IMO the difference between T operator auto() and auto operator T() is a bit of a bikeshed, but I don't mind bikeshedding that much. If you did mean that, then communication fail by at least one of us :P

Nicol Bolas

unread,
Nov 22, 2012, 9:21:16 PM11/22/12
to std-pr...@isocpp.org

Yeah, that's what I was going for. That there is an `operator auto`, just like an `operator ()`, `operator +` and so forth. Thus, the type is the return value.

The point being that `auto operator auto` means the same thing as `auto operator +` or `auto operator()`. It's a consistency thing.

DeadMG

unread,
Nov 22, 2012, 9:23:22 PM11/22/12
to std-pr...@isocpp.org
If that's the only quibble you could think of, then I'm a happy man. In fact, you might well be right that it's a superior way of going about it. I simply thought that auto operator T() is more consistent with the semantics of what's going on, namely, that it's a conversion operator, and the auto specifies the conditions in which the conversion is valid- all the time- similar to explicit operator T() and implicit operator T().

DeadMG

unread,
Nov 22, 2012, 9:33:24 PM11/22/12
to std-pr...@isocpp.org
It occurs to me that expression templates is hardly the only place that this could prove useful. Consider an implementation which changed the address-of operator to yield a different type depending on where the object was, and then added an automatic conversion to a normal pointer. Would it be possible to implement some aliasing optimizations based on that? After all, logically, the address of a member of a class cannot ever alias the address of a member of a function parameter.

Nicol Bolas

unread,
Nov 22, 2012, 9:34:39 PM11/22/12
to std-pr...@isocpp.org
On Thursday, November 22, 2012 6:23:22 PM UTC-8, DeadMG wrote:
If that's the only quibble you could think of, then I'm a happy man. In fact, you might well be right that it's a superior way of going about it. I simply thought that auto operator T() is more consistent with the semantics of what's going on, namely, that it's a conversion operator, and the auto specifies the conditions in which the conversion is valid- all the time- similar to explicit operator T() and implicit operator T().

Perhaps. But then, what happens if you want to do this:

auto operator std::string() {return {5};}

You can't, because a std::string cannot be implicitly constructed from a single integer. You have to use the typename:

auto operator std::string() {return std::string{5};}

Whereas, if we have functions that can have automatically-deduced return values:

auto operator auto() {return std::string{5};}

It's clear what it returns; it's only specified once.

I understand wanting to designate one of the conversion operators as `auto`, which would be used in such contexts. But I don't see the whole operation as a conversion operation. It feels more like a special operation (type-deduction) that ends up with a new type and therefore requires machinery not-entirely-unlike conversion. But, as you point out, it's more of a bikeshed issue.

Xeo

unread,
Nov 22, 2012, 9:39:11 PM11/22/12
to std-pr...@isocpp.org
auto operator std::string() { return decltype(*this){5}; }

;)

Nicol Bolas

unread,
Nov 22, 2012, 9:56:15 PM11/22/12
to std-pr...@isocpp.org


On Thursday, November 22, 2012 6:39:12 PM UTC-8, Xeo wrote:
auto operator std::string() { return decltype(*this){5}; }

;)

That would only work if `this` actually was `std::string`. This is an object which is meant to be converted into a std::string; it isn't part of it.

Xeo

unread,
Nov 22, 2012, 10:03:21 PM11/22/12
to std-pr...@isocpp.org
On Friday, November 23, 2012 3:56:15 AM UTC+1, Nicol Bolas wrote:
That would only work if `this` actually was `std::string`. This is an object which is meant to be converted into a std::string; it isn't part of it.

According to the proposal, the auto operator should also be picked for decltype, and since the declaration (auto operator std::string) is complete at that point, I don't see the problem.

Xeo

unread,
Nov 22, 2012, 10:05:20 PM11/22/12
to std-pr...@isocpp.org
It was just meant as a joke anyways, nothing to get hung up on.

Xavi Gratal

unread,
Oct 12, 2013, 1:40:21 PM10/12/13
to std-pr...@isocpp.org
I know it's been a while, but has this gotten anywhere near an actual proposal?
I have a matrix library on the works where this could be extremely useful. I'm currently having all my expression templates uncopiable and unmoveable so that users can't get references to expired temporaries by using auto.

Also, the syntax

T operator auto() {}

looks good to me, but is it really necessary? It seems to do two things at once: tell which type should be deduced by auto and how to convert to that type. Why not just tell which type it should be converted to with something like

typedef T auto;

and then the actual conversion would happen as usual, via a constructor in T, or a separate operator T, which might be optionally declared as explicit.
Any thoughts? Anyone still interested in this?

David Krauss

unread,
Oct 13, 2013, 1:48:05 AM10/13/13
to std-pr...@isocpp.org
On 10/13/13 1:40 AM, Xavi Gratal wrote:
> I know it's been a while, but has this gotten anywhere near an actual
> proposal?
> I have a matrix library on the works where this could be extremely useful.
> I'm currently having all my expression templates uncopiable and unmoveable
> so that users can't get references to expired temporaries by using auto.

A user can still bypass that using a "universal reference," auto &&,
which some might already find idiomatic.

> Also, the syntax
>
> T operator auto() {}
>
> looks good to me, but is it really necessary? It seems to do two things at
> once: tell which type should be deduced by auto and how to convert to that
> type. Why not just tell which type it should be converted to with something
> like
>
> typedef T auto;
>
> and then the actual conversion would happen as usual, via a constructor in
> T, or a separate operator T, which might be optionally declared as explicit.
> Any thoughts? Anyone still interested in this?

The member typedef auto does look cleaner to me. The differences I see
are that

- With two different functions, you have potentially different
behaviors, which would generally be bad.
- If operator auto can be overloaded on const and reference
qualification, that leads to divergent type deduction and behavior. Or,
if it cannot, then the behavior is inconsistent with other conversion
functions. (I haven't checked the proposal for this issue.)

Recently I suggested (on this board, no formal proposal) to forbid a
type from being used to declare a named object. The mechanism is
destructor overloading. A named object would be destroyed by an "lvalue"
destructor.

https://groups.google.com/a/isocpp.org/forum/#!topic/std-proposals/2tfKrgdKvfo

The behavior would be limited to a compile-time error such as a
static_assert, not an automatic fix. As a library author, what do you
think of that?

Message has been deleted

snk_kid

unread,
Oct 13, 2013, 6:00:43 AM10/13/13
to std-pr...@isocpp.org
I see one potential issue with such a proposal, in some cases this breaks the ability to use local type inference with intermediate values of expression templates where a user may not want the expression to be immediately evaluated at the point of construction but always will because the library writer has overridden the behavior of auto to do so.

This also brings us back to the originally problem of having to explicitly define what the type of a potentially complex template expression is for these intermediate values or force users to write their expressions all inline, in one expression which could be quite complex and unreadable.

So I think in practice there is very little reason to write/provide such overloads for most expression template libraries, of-course it is dependent on the context of what the specific expression template library is trying to achieve.

There has a to better way than having to change/override the behavior of type inference engine, maybe library vendors should just define a helper 'eval' function for this specific case and at least this makes it quite clear what is going on with the expression and what the deduced type will be e.g.:

auto delta = j - i; // the type of 'delta' is some expression template type
auto x = eval( delta + z ); // the type of 'x' is the final evaluated/reduced type.

Personally I'm not a fan of such language proposals as there tend to try to solve very specific problems to workaround solutions to language deficiencies for more general problems.

What I mean by this is expression templates really is workaround library solution to the problems of either: before C++ had built-in move semantics, or missing languages features that allow people to write efficient embedded domain-specific languages naturally in C++.

DeadMG

unread,
Oct 13, 2013, 6:09:25 AM10/13/13
to std-pr...@isocpp.org
 You're making some pretty core errors here.

this breaks the ability to use local type inference with intermediate values of expression templates where a user may not want the expression to be immediately evaluated at the point of construction but always will because the library writer has overridden the behavior of auto to do so.

If you know so much about my library that you want to do this when I have decided that it is not safe in general, then you can go and name the exact intermediate type for yourself. It's my library. I know whether or not I have references to temporaries in my code. You don't. If I don't have references to temporaries in my code then why would I override operator auto like this? It wouldn't make sense.

one expression which could be quite complex and unreadable.

That's the current situation anyway, if you want to get full expression templateness. Because you can't store the expression template result. Because they contain references to various temporaries. 

missing languages features that allow people to write efficient embedded domain-specific languages naturally in C++.

You'd need some hardcore reflection to do better than expression templates. 

Joel Falcou

unread,
Oct 13, 2013, 6:19:12 AM10/13/13
to std-pr...@isocpp.org

snk_kid

unread,
Oct 13, 2013, 7:04:28 AM10/13/13
to std-pr...@isocpp.org
 You're making some pretty core errors here.

this breaks the ability to use local type inference with intermediate values of expression templates where a user may not want the expression to be immediately evaluated at the point of construction but always will because the library writer has overridden the behavior of auto to do so.

If you know so much about my library that you want to do this when I have decided that it is not safe in general, then you can go and name the exact intermediate type for yourself. It's my library. I know whether or not I have references to temporaries in my code. You don't. If I don't have references to temporaries in my code then why would I override operator auto like this? It wouldn't make sense.

Please don't misunderstand me, I'm talking in a broader sense here. I understand your frustration because I've had similar issues myself using the Eigen linear algbra library where in some cases I didn't want auto to deduce the type to be the expression template type but I don't I always want that too happen. What I'm trying to say is there must be a better way to solve this than having too mess with and complicate the type inference rules.
 
missing languages features that allow people to write efficient embedded domain-specific languages naturally in C++.

You'd need some hardcore reflection to do better than expression templates. 

I'm making assumptions as too what you mean here but if I understood what you're implying this isn't the only way and if you wanted to go the (semi-) compile-time route then compile-time reflection wouldn't be enough alone.

For specifically writing embedded DSLs (like Boost.Spirit) as far as I am aware there are usually 2 directions you can go to solve this and both can be mixed together if both are available.

One approach is support for a real AST based macro systems and code quotations (and maybe support for syntax extensions for the more extreme cases).

The second approach requires no AST macro system and can be a complete pure library approach but this requires a combination of language features, first-class functions & operators, more flexible support for defining operators such as specifying fixity & precedence levels and more symbols or a small set of symbols but can be combined to define new ones. This can be further enhanced & more flexible with additional language features such as parametric & higher-kinded/rank polymorphism, support for making expressions lazy, algebraic data-types and pattern matching.

The latter approach requires no reflection system what so ever :)

Haskell is an example programming language which has support for both approaches and the latter approach is the most common easiest way to write eDSL in Haskell. DSL libraries like Boost.Spirit are very easy and natural to write as a pure libraries in Haskell.

eDSL expression template libraries effectively are a workaround solution to emulate most of those lacking features in the second approach. If we had most of those features in the second approach we would not need to write expression templates at all so maybe we would never have this issue with how expression template libraries interact with local type inference.

xavi

unread,
Oct 13, 2013, 11:17:04 AM10/13/13
to std-pr...@isocpp.org



2013/10/13 David Krauss <pot...@gmail.com>

On 10/13/13 1:40 AM, Xavi Gratal wrote:
I know it's been a while, but has this gotten anywhere near an actual
proposal?
I have a matrix library on the works where this could be extremely useful.
I'm currently having all my expression templates uncopiable and unmoveable
so that users can't get references to expired temporaries by using auto.

A user can still bypass that using a "universal reference," auto &&, which some might already find idiomatic.

True, and that's unfortunate 

Also, the syntax

T operator auto() {}

looks good to me, but is it really necessary? It seems to do two things at
once: tell which type should be deduced by auto and how to convert to that
type. Why not just tell which type it should be converted to with something
like

typedef T auto;

and then the actual conversion would happen as usual, via a constructor in
T, or a separate operator T, which might be optionally declared as explicit.
Any thoughts? Anyone still interested in this?

The member typedef auto does look cleaner to me. The differences I see are that

- With two different functions, you have potentially different behaviors, which would generally be bad.
- If operator auto can be overloaded on const and reference qualification, that leads to divergent type deduction and behavior. Or, if it cannot, then the behavior is inconsistent with other conversion functions. (I haven't checked the proposal for this issue.)

Recently I suggested (on this board, no formal proposal) to forbid a type from being used to declare a named object. The mechanism is destructor overloading. A named object would be destroyed by an "lvalue" destructor.

https://groups.google.com/a/isocpp.org/forum/#!topic/std-proposals/2tfKrgdKvfo

The behavior would be limited to a compile-time error such as a static_assert, not an automatic fix. As a library author, what do you think of that?

That sounds like a good feature to have, even if only because I don't see any particular reason why destructors cannot be ref-qualified (but maybe there is, I haven't looked at it in detail).
However, I am not sure it solves the problem you mention of using auto && or const auto &, because I am not sure how an object whose life has been extended because of being bound to a const reference would be destroyed. The object itself is not named, so it should probably still be destroyed as an rvalue.
Also, it could prevent auto working in unexpected ways, but it wouldn't allow auto to be used in the right way, which is part of the goal of this proposal.

--

--- 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-proposals+unsubscribe@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/.

xavi

unread,
Oct 13, 2013, 11:34:33 AM10/13/13
to std-pr...@isocpp.org
Even in a matrix library, which is one of the most obvious cases for expression templates, move semantics don't completely solve the problem and I believe that expression templates will also be necessary as long as C++ looks anything like C++:

- Some small (and not so small) matrices can be implemented in the stack, thus making move behave exactly like copy.
- If you're doing something like a+b+c, it is usually faster to run a single loop than to first do a+b and then add c, even if there is no copy involved.
- Some expressions might have completely different implementations depending on how they are combined. For example, in my matrix library, I use the syntax a.I() to represent the inverse of a matrix. Of course, to compute b*a.I(), there are better ways to do it than to compute the inverse and then multiply, so I only want to actually compute the inverse if it is not involved on a product, so I need an expression template until I know what happens to it. Then, if I do auto b=a.I(), I actually want to compute the inverse and b should be a matrix, not the expression template, which might contain references to temporaries.

It's just ugly to force users to use eval() constantly, and sometimes redundantly, because in many cases to know whether eval is necessary or not requires knowing too many about the implementation.

So I believe that even if this functionality will only be used by implementors of a particular type of library, it will benefit all the users of these libraries, and it will isolate them from expression templates, which are just implementation details.


2013/10/13 Joel Falcou <joel....@gmail.com>
 Thanks for the link! I hadn't seen it. 
I especially like the using syntax, which is almost identical to the typedef I proposed, but it looks a bit more C++11. 
I don't think the explicit notation is necessary at all. I would propose that the overridden conversion works only for auto (and maybe const auto& and similar when they extend the lifetime of a temporary) but they don't apply to things like decltype(a+b) or even decltype(auto), making it easy to obtain the real type of the expression.
Also, I don't think that they have to apply to template argument deduction (as suggested in section 5), because the main argument for this proposal is that it would prevent having dangling references to dead temporaries, and this wouldn't be a problem when passing the expression templates to other functions. Also, that would make the implementation of the library dealing with the expression templates much more cumbersome, since in there you actually want the deduced types to be the expression templates themselves.
Finally, I don't share the compatibility concerns expressed in section 4, since it wouldn't change the meaning of any currently valid program.

Joel Falcou

unread,
Oct 13, 2013, 12:15:29 PM10/13/13
to std-pr...@isocpp.org
On 13/10/2013 17:34, xavi wrote:
- Some expressions might have completely different implementations depending on how they are combined. For example, in my matrix library, I use the syntax a.I() to represent the inverse of a matrix. Of course, to compute b*a.I(), there are better ways to do it than to compute the inverse and then multiply, so I only want to actually compute the inverse if it is not involved on a product, so I need an expression template until I know what happens to it. Then, if I do auto b=a.I(), I actually want to compute the inverse and b should be a matrix, not the expression template, which might contain references to temporaries.

Ditto here. In NT2, the HPC matlab like EDSL C++library my laboratory tram and company co-author, we have a generalized pass to retype large part of EDSL AST if they match domain specific algortihm. This is somethign move semantic can do. EDSL usign expression templates still are useful for AST rewriting as you say.

 

It's just ugly to force users to use eval() constantly, and sometimes redundantly, because in many cases to know whether eval is necessary or not requires knowing too many about the implementation.

It also breaks the intuitive API EDSL brings to the table.



2013/10/13 Joel Falcou <joel....@gmail.com>
Did you look at http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3748.pdf ?

 Thanks for the link! I hadn't seen it. 
I especially like the using syntax, which is almost identical to the typedef I proposed, but it looks a bit more C++11. 
I don't think the explicit notation is necessary at all. I would propose that the overridden conversion works only for auto (and maybe const auto& and similar when they extend the lifetime of a temporary) but they don't apply to things like decltype(a+b) or even decltype(auto), making it easy to obtain the real type of the expression.
Also, I don't think that they have to apply to template argument deduction (as suggested in section 5), because the main argument for this proposal is that it would prevent having dangling references to dead temporaries, and this wouldn't be a problem when passing the expression templates to other functions. Also, that would make the implementation of the library dealing with the expression templates much more cumbersome, since in there you actually want the deduced types to be the expression templates themselves.
Finally, I don't share the compatibility concerns expressed in section 4, since it wouldn't change the meaning of any currently valid program.

We'll probably have to revise this document so thanks for your insight.

MJanes

unread,
Oct 14, 2013, 4:12:23 AM10/14/13
to std-pr...@isocpp.org
Il giorno domenica 13 ottobre 2013 17:34:33 UTC+2, Xavi Gratal ha scritto:
So I believe that even if this functionality will only be used by implementors of a particular type of library, it will benefit all the users of these libraries, and it will isolate them from expression templates, which are just implementation details.

sorry for the intrusion, but

as a user of these libraries, I'd say that expression templates are not implementation details. I mean, <knowing> that some expression will be evaluated lazily, exploiting every possible bit of compile-time information available ( eventually extending the library on my own as well ), is the very reason I'd use such a library in the first place.
So, instead of hijacking the auto deduction rules, why not trying to make expression templates safer to use as non-temporaries instead ( a feature I'd find useful on its own BTW ) ?
For example, by marking a constructor as, say, "persist MyClass( ... )" then, given an initializing expression of an &&/const& reference R, temporaries of type MyClass ( whereever they appear ) could be automatically life-extended to the scope of R.

xavi

unread,
Oct 14, 2013, 5:40:42 AM10/14/13
to std-proposals


2013/10/14 MJanes <max...@gmail.com>

Il giorno domenica 13 ottobre 2013 17:34:33 UTC+2, Xavi Gratal ha scritto:

So I believe that even if this functionality will only be used by implementors of a particular type of library, it will benefit all the users of these libraries, and it will isolate them from expression templates, which are just implementation details.

sorry for the intrusion, but
Any comment is appreciated! 

as a user of these libraries, I'd say that expression templates are not implementation details. I mean, <knowing> that some expression will be evaluated lazily, exploiting every possible bit of compile-time information available ( eventually extending the library on my own as well ), is the very reason I'd use such a library in the first place.
I'd say there are two different things here. Whether an expression will be evaluated lazily is something the author should provide in the documentation and/or the description of the interfaces. How this is actually achieved internally, that's an implementation detail users should not care about, because the author should be able to change it in order to improve the library, as long as it doesn't modify the API (interface description included). If you want to modify the library, or extend it in ways that are not supported through the provided interfaces, that's perfectly ok, but that doesn't mean they are not implementation details, it means that you want to look at the implementation details.
 
So, instead of hijacking the auto deduction rules, why not trying to make expression templates safer to use as non-temporaries instead ( a feature I'd find useful on its own BTW ) ?
For example, by marking a constructor as, say, "persist MyClass( ... )" then, given an initializing expression of an &&/const& reference R, temporaries of type MyClass ( whereever they appear ) could be automatically life-extended to the scope of R. 
I don't know how hard it would be to implement this (my guess is not easy). Also, that should be an attribute of the reference, not the class, because sometimes you want to store references to types that are not even part of the library. It would also only work for header-only implementations, since the compiler should need to track all temporaries to see which ones are assigned to the variables.
It also only solves the problem with real temporaries, not with objects which are destroyed or assigned to. Consider the example:

auto c=a+b; //b is an expression template containing references to a and b
a=b; //a changed value!! that's certainly not what users of the library would expect

Finally, dangling references is not the only reason why someone would want to hide expression templates. Most expression templates, for example one that refers to a matrix product are supposed to be consumed only once, for efficiency reasons. If the user had a reference to an expression template, they could use it in several expressions, causing the same product to be computed multiple times.

--
 
---
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.

DeadMG

unread,
Oct 14, 2013, 5:56:25 AM10/14/13
to std-pr...@isocpp.org
Not to mention the issue of stuff like (a + b).c_str();. If you use the persist idea, then you have to re-implement the entire interface of your class. An automatic conversion would not require this.

MJanes

unread,
Oct 14, 2013, 6:50:51 AM10/14/13
to std-pr...@isocpp.org
Il giorno lunedì 14 ottobre 2013 11:40:42 UTC+2, Xavi Gratal ha scritto:
I'd say there are two different things here. Whether an expression will be evaluated lazily is something the author should provide in the documentation and/or the description of the interfaces. How this is actually achieved internally, that's an implementation detail users should not care about, because the author should be able to change it in order to improve the library, as long as it doesn't modify the API (interface description included). If you want to modify the library, or extend it in ways that are not supported through the provided interfaces, that's perfectly ok, but that doesn't mean they are not implementation details, it means that you want to look at the implementation details.


ok, but put in another way, IMO I <expect> from an expression template library to allow me adding my own functionality ( say, if I come up with an algorithm optimizing the product of matrices of certain kind, say, belonging to specific algebras ) and hence I expect that the types involved in an expression template at least are models of a prescribed concept or, in other words, they are not "internal" to the library to some degree, and hence are accessible by the user. For this reason, I don't consider immoral for me to say "auto expr = ..." meaning "give to this <unevaluated expression> the name expr".
 
 Also, that should be an attribute of the reference, not the class, because sometimes you want to store references to types that are not even part of the library.

yeah, that's a problem; may I ask you to make an example of such a use case ( having no reasonable workaround ) ?
 
a=b; //a changed value!! that's certainly not what users of the library would expect

why ? ideally, in my mind a lazy-expression is exactly a reference-like object, so if I write "auto c=a+b; do_something_with(c); a = foo(); do_something_with(c);" I would expect the behavior of do_something_with to change accordingly after the "a" assignment.
 
Finally, dangling references is not the only reason why someone would want to hide expression templates. Most expression templates, for example one that refers to a matrix product are supposed to be consumed only once, for efficiency reasons. If the user had a reference to an expression template, they could use it in several expressions, causing the same product to be computed multiple times.

the library author could give two flavors of multiplications, allowing single and multiple invocations, respectively. For the single invocation case, it would be said in the documentation what happens when multiple invocations occur ( including UB ). The library author and/or the user will decide which one should be the default multiplication and which one should be explicitly requested instead ( say, auto expr = multicallable( ... ); ).

so, I understand that the persist idea cannot solve these problems <given the way expression templates libraries are currrently designed>. But, the problem is, could a slightly different design/semantics + better temporary lifetime management do it ? ( I'm also thinking at alternative solutions like the Ref-qualified destructors idea ).

@DeadMG

Not to mention the issue of stuff like (a + b).c_str();.

could you elaborate ? to clarify, the hypothetical persist constructor would trigger lifetime extension only in those initialization where a temporary gets bound to a reference ( say, "T&& t = T(V());", here, if V() is persist, than its lifetime gets bound to t ... ).

Joel Falcou

unread,
Oct 14, 2013, 7:04:50 AM10/14/13
to std-pr...@isocpp.org
On 14/10/2013 12:50, MJanes wrote:
> The library author could give two flavors of multiplications, allowing
> single and multiple invocations, respectively. For the single
> invocation case, it would be said in the documentation what happens
> when multiple invocations occur ( including UB ). The library author
> and/or the user will decide which one should be the default
> multiplication and which one should be explicitly requested instead (
> say, auto expr = multicallable( ... ); ).
>

Boost.Proto (an EDSL toolkit from boost) has a deep_copy function that
turns a lazy stuff into a copy of all its contents, eliminating the
issue of lifetime at the cost of more copies. Is it somethign akin to
what you have in mind ?

Cassio Neri

unread,
Oct 14, 2013, 12:18:53 PM10/14/13
to std-pr...@isocpp.org


On Saturday, October 12, 2013 6:40:21 PM UTC+1, Xavi Gratal wrote:
I know it's been a while, but has this gotten anywhere near an actual proposal?

It seems to me that this is n3748:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3748.pdf

HTH,
Cassio.
Reply all
Reply to author
Forward
0 new messages