Add a function "as_const" to turn a non-const reference into a const one

504 views
Skip to first unread message

Jonathan Coe

unread,
Aug 16, 2014, 7:05:41 AM8/16/14
to std-pr...@isocpp.org
Sometimes it's desirable to use const methods on a non-const object.
This can be done with const_cast, but const_cast can equally well be used to do potentially unsafe things and is often flagged up in code reviews or banned outright by coding standards/pre-commit scripts.

I propose the addition of a simple function:

template <typename T>
std::as_const(T&& t) 
  return const_cast<const T&&>(t); 
}

which can be used to perform the safe const-cast operation.

David Krauss

unread,
Aug 16, 2014, 9:23:57 AM8/16/14
to std-pr...@isocpp.org
On 2014–08–16, at 7:05 PM, Jonathan Coe <jonath...@gmail.com> wrote:

Sometimes it's desirable to use const methods on a non-const object.
This can be done with const_cast, but const_cast can equally well be used to do potentially unsafe things and is often flagged up in code reviews or banned outright by coding standards/pre-commit scripts.

It can also be done with static_cast.

The proposal is still nice.


Geoffrey Romer

unread,
Aug 16, 2014, 3:01:27 PM8/16/14
to std-pr...@isocpp.org

implicit_cast is nice for situations like this, if you have it— static_cast can still do unwelcome things if you're not careful. Speaking of which, we ought to standardize implicit_cast.

--

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

Eelis

unread,
Aug 16, 2014, 3:43:40 PM8/16/14
to std-pr...@isocpp.org
On 2014-08-16 21:01, 'Geoffrey Romer' via ISO C++ Standard - Future
Proposals wrote:
> we ought to standardize implicit_cast.

+1


Zhihao Yuan

unread,
Aug 16, 2014, 4:03:46 PM8/16/14
to std-pr...@isocpp.org
On Sat, Aug 16, 2014 at 3:01 PM, 'Geoffrey Romer' via ISO C++ Standard - Future Proposals <std-pr...@isocpp.org> wrote:

implicit_cast is nice for situations like this, if you have it— static_cast can still do unwelcome things if you're not careful. Speaking of which, we ought to standardize implicit_cast.

Implicit conversion cab still do unwelcome things like
narrowing, unsigned to signed, etc.

And it's verbose, especially when T is verbose or
it's tricky to const qualify T.  to/as_const, to/as_unsigned
are convenient type deduced interfaces, and not possible
to go wrong.

--
Zhihao Yuan, ID lichray
The best way to predict the future is to invent it.
___________________________________________________
4BSD -- http://bit.ly/blog4bsd

Andrew Tomazos

unread,
Aug 16, 2014, 4:46:45 PM8/16/14
to std-pr...@isocpp.org
On Sat, Aug 16, 2014 at 1:05 PM, Jonathan Coe <jonath...@gmail.com> wrote:
Sometimes it's desirable to use const methods on a non-const object.

For example?

Nicola Gigante

unread,
Aug 17, 2014, 3:56:13 AM8/17/14
to std-pr...@isocpp.org, Nicola Gigante
std::map::operator[]

Bye,
Nicola

Andrew Tomazos

unread,
Aug 17, 2014, 4:49:43 AM8/17/14
to std-pr...@isocpp.org
std::map::operator[] is not a "const method".

Nicola Gigante

unread,
Aug 17, 2014, 5:53:46 AM8/17/14
to std-pr...@isocpp.org, Nicola Gigante

Il giorno 17/ago/2014, alle ore 10:49, Andrew Tomazos <andrew...@gmail.com> ha scritto:
>
> std::map::operator[] is not a "const method".
>

Wops, sorry..

vadim.pet...@gmail.com

unread,
Aug 17, 2014, 6:29:46 AM8/17/14
to std-pr...@isocpp.org
+1 to as_signed/as_unsigned, very convenient to deal with "signed/unsigned mismatch" warnings and to use with auto

Jonathan Coe

unread,
Aug 17, 2014, 6:57:16 AM8/17/14
to std-pr...@isocpp.org
+1 
to as _signed as_unsigned
--

David Krauss

unread,
Aug 17, 2014, 8:08:20 PM8/17/14
to std-pr...@isocpp.org
as_signed sounds like a recipe for overflow. If you want to simply defeat the warnings, disable them with a compiler flag, not by explicitly translating them to UB. boost::numeric_cast could encourage better practices.

One use case for as_const would be a user-defined container, as an alternative to implementing the boilerplate cbegin() and cend(). I have to admit, though, that at the cost of one extra line you can always just declare a const reference. (If the variable is a temporary, it could be trickier, but when does a temporary need to be const?)

vadim.pet...@gmail.com

unread,
Aug 18, 2014, 4:11:26 AM8/18/14
to std-pr...@isocpp.org
>as_signed sounds like a recipe for overflow

No more than any other signed arithmetic.
A typical example of use:

std::string getWordForm(const Sentence& sent, Sentence::size_type abs_pos, Sentence::difference_type shift)
{
      std
::string form;
     
auto pos = as_signed(abs_pos) + shift;
     
if (pos >= 0 && as_unsigned(pos) < sent.size()) form = sent[as_unsigned(pos)].form();
     
return form;
}

To think about overflow here would be just excessive.
(And look at the dangerous operator+, I'd better use boost::checked_add!)

>to simply defeat the warnings, disable them with a compiler flag
I'd prefer the conversions to be explicit, and doing static_casts is unwieldy.

Myriachan

unread,
Aug 18, 2014, 10:14:54 PM8/18/14
to std-pr...@isocpp.org
On Monday, August 18, 2014 1:11:26 AM UTC-7, vadim.pet...@gmail.com wrote:
>as_signed sounds like a recipe for overflow

No more than any other signed arithmetic.
A typical example of use:


These are very problematic.  Let's say you wrote code like this:

// Make sure that this multiplication is unsigned so that when it overflows,
// it is well-defined as wrapping around.
template <typename X>
X wrap_square
(X x)
{
   
return static_cast<X>(as_unsigned(x) * as_unsigned(x));
}

...except that this is wrong: it has undefined behavior on 32-bit machines when X is std::uint16_t.  Despite the attempt at doing unsigned arithmetic, it still is doing signed arithmetic.  Worse yet, this is platform-specific.

(This is why I downright hate signed integer overflow being undefined.)

Melissa

Greg Marr

unread,
Aug 18, 2014, 10:58:53 PM8/18/14
to std-pr...@isocpp.org
\On Saturday, August 16, 2014 7:05:41 AM UTC-4, Jonathan Coe wrote:
Sometimes it's desirable to use const methods on a non-const object.
This can be done with const_cast, but const_cast can equally well be used to do potentially unsafe things and is often flagged up in code reviews or banned outright by coding standards/pre-commit scripts.

What am I missing here?  You can already use const methods on non-const objects without a cast.  Are you trying to select a const overload in favor of a non-const overload?

Tom Honermann

unread,
Aug 18, 2014, 11:08:49 PM8/18/14
to std-pr...@isocpp.org
On 08/18/2014 10:14 PM, Myriachan wrote:
On Monday, August 18, 2014 1:11:26 AM UTC-7, vadim.pet...@gmail.com wrote:
>as_signed sounds like a recipe for overflow

No more than any other signed arithmetic.
A typical example of use:


These are very problematic.  Let's say you wrote code like this:

// Make sure that this multiplication is unsigned so that when it overflows,
// it is well-defined as wrapping around.
template <typename X>
X wrap_square
(X x)
{
   
return static_cast<X>(as_unsigned(x) * as_unsigned(x));
}

...except that this is wrong: it has undefined behavior on 32-bit machines when X is std::uint16_t.  Despite the attempt at doing unsigned arithmetic, it still is doing signed arithmetic.  Worse yet, this is platform-specific.

Would it be terrible if as_unsigned() implicitly applied promotions?  Alternatively, as_unsigned_promoted(x). 

Tom.

Andrew Tomazos

unread,
Aug 18, 2014, 11:12:57 PM8/18/14
to std-pr...@isocpp.org
I share your skepticism, and am still waiting for a motivating code example of where as_const is needed.


Zhihao Yuan

unread,
Aug 18, 2014, 11:59:38 PM8/18/14
to std-pr...@isocpp.org
On Mon, Aug 18, 2014 at 11:12 PM, Andrew Tomazos <andrew...@gmail.com> wrote:

I share your skepticism, and am still waiting for a motivating code example of where as_const is needed.

  for (x : as_const(v))
  {
    // got x from begin const / end const
  }

  struct Foo
  {
    T& foo()
    {
      // maybe more useful with as_non_const?
      return const_cast<T&>(as_const(*this).foo());
    }

    T const& foo() const
    {
      // ...
    }
  };

Andrew Tomazos

unread,
Aug 19, 2014, 1:06:05 AM8/19/14
to std-pr...@isocpp.org
On Tue, Aug 19, 2014 at 5:59 AM, Zhihao Yuan <z...@miator.net> wrote:
On Mon, Aug 18, 2014 at 11:12 PM, Andrew Tomazos <andrew...@gmail.com> wrote:

I share your skepticism, and am still waiting for a motivating code example of where as_const is needed.

  for (x : as_const(v))
  {
    // got x from begin const / end const
  }

I think this is better:

   for (const auto& x : v) { ... }

But STL also has this in the works:

   for (const x : v) { ... }

Also, if v is a temporary, will your version be lifetime extended?

  struct Foo
  {
    T& foo()
    {
      // maybe more useful with as_non_const?
      return const_cast<T&>(as_const(*this).foo());
    }

    T const& foo() const
    {
      // ...
    }
  };


Presumably, this is if there is a private lvalue being returned:

struct Foo
{
    T& getFoo() { return foo; }
    const T& getFoo() const { return foo; }

private:

    T foo;
};

I still suspect needing as_const is indicative of a design flaw in the surrounding code.

Jonathan Coe

unread,
Aug 19, 2014, 2:11:43 AM8/19/14
to std-pr...@isocpp.org
Yes. 

Myriachan

unread,
Aug 19, 2014, 3:25:34 PM8/19/14
to std-pr...@isocpp.org

Linked list iterators can be more complicated if they have to account for the possibility of list elements being deleted during the current iteration.  If you want to iterate a linked list, and don't need to change anything in the list, but you're in a non-const context for that list, you would get a slight performance boost from static_casting the list to const type--or using this proposed std::as_constcbegin and cend work, but not with C++11 "for each" syntax, right?
How would this work?  The promoted type of std::uint16_t on 32-bit platforms is signed int, hence the current insanity.  We'd want as_unsigned_promoted<std::uint16_t> to return unsigned int?

Melissa

Nevin Liber

unread,
Aug 19, 2014, 3:45:53 PM8/19/14
to std-pr...@isocpp.org
On 19 August 2014 14:25, Myriachan <myri...@gmail.com> wrote:
On Monday, August 18, 2014 8:12:57 PM UTC-7, Andrew Tomazos wrote:
On Tue, Aug 19, 2014 at 4:58 AM, Greg Marr <greg...@gmail.com> wrote:
What am I missing here?  You can already use const methods on non-const objects without a cast.  Are you trying to select a const overload in favor of a non-const overload?

I share your skepticism, and am still waiting for a motivating code example of where as_const is needed.

Linked list iterators can be more complicated if they have to account for the possibility of list elements being deleted during the current iteration. 

I do not believe that is a requirement of any iterator in the standard library.  Sure, you might have a debugging library which does that, but performance considerations and debugging library typically don't go together.
--
 Nevin ":-)" Liber  <mailto:ne...@eviloverlord.com(847) 691-1404

Tom Honermann

unread,
Aug 19, 2014, 9:45:25 PM8/19/14
to std-pr...@isocpp.org
Exactly. I'm not saying it is necessarily a good idea, but I believe it
would address the undefined behavior in the above test case.

Tom.

David Krauss

unread,
Aug 19, 2014, 10:31:18 PM8/19/14
to std-pr...@isocpp.org

On 2014–08–20, at 9:45 AM, Tom Honermann <thone...@coverity.com> wrote:

Exactly.  I'm not saying it is necessarily a good idea, but I believe it would address the undefined behavior in the above test case.

Usual arithmetic conversions are not a defense against overflow. The rule is designed to help the language model CPUs that only perform computations at the width of the ALU. The idea is that the machine ABI sets int to the width of the ALU, and then fewer bit-masking operations are required. On a 16-bit machine, or a 32-bit architecture supporting 16-bit operations such as M68K, int is not likely to be wider than int16_t. (On an 8-bit machine, this is also true, and furthermore by-default arithmetic promotion may be counterproductive as the result is wider than the ALU, such that the optimizer must “undo” it when it is unconditionally narrowed again.)

I can see a function like promote() being helpful only because implicit promotion is a bear for generic numerics. Few people ever code that carefully though, and it’s easy enough to define for yourself, or to annotate such expressions with the unary + operator.

Tom Honermann

unread,
Aug 20, 2014, 10:15:08 AM8/20/14
to std-pr...@isocpp.org
On 08/19/2014 10:28 PM, David Krauss wrote:
>
> On 2014–08–20, at 9:45 AM, Tom Honermann <thone...@coverity.com
> <mailto:thone...@coverity.com>> wrote:
>
>> Exactly. I'm not saying it is necessarily a good idea, but I believe
>> it would address the undefined behavior in the above test case.
>
> Usual arithmetic conversions are not a defense against overflow.

The suggestion wasn't intended to aid in defense against overflow, but
rather to avoid undefined behavior when an overflow occurs. The example
that Melissa provided included this comment:
// Make sure that this multiplication is unsigned so that when it
overflows,
// it is well-defined as wrapping around.

> I can see a function like promote() being helpful only because implicit
> promotion is a bear for generic numerics. Few people ever code that
> carefully though, and it’s easy enough to define for yourself, or to
> annotate such expressions with the unary + operator.

Agreed. The suggested as_unsigned_promoted(x) could then be replaced
by, or implemented as, as_unsigned(promoted(x)). I think the utility of
this is limited by the fact that one needs to understand promotion rules
and undefined behavior with respect to signed arithmetic in order to
know when to protect against unintended behavior. In other words, it
probably doesn't help people from writing incorrect code in the first
place (though its use *might* help to prevent introducing bugs during
maintenance).

Tom.

Zhihao Yuan

unread,
Aug 20, 2014, 9:29:12 PM8/20/14
to std-pr...@isocpp.org
On Tue, Aug 19, 2014 at 1:06 AM, Andrew Tomazos <andrew...@gmail.com> wrote:

  for (x : as_const(v))
  {
    // got x from begin const / end const
  }

I think this is better:

   for (const auto& x : v) { ... }

But STL also has this in the works:

   for (const x : v) { ... }

There is a difference.  In your example, the iteration is still taken on
the iterator, but my example give you const_iterator -- they are not
required to be different only by qualification!  Even in a standard library,
they can be unrelated types in debug mode for catch more iterator bugs.

And I assume that's one of the reason why such a syntax is not
proposed in STL's paper.


Also, if v is a temporary, will your version be lifetime extended?

Yes.  as_const(v) does not put v out of full expression.

Thiago Macieira

unread,
Aug 20, 2014, 11:44:18 PM8/20/14
to std-pr...@isocpp.org
On Wednesday 20 August 2014 21:29:10 Zhihao Yuan wrote:
> > I think this is better:
> > for (const auto& x : v) { ... }
> >
> > But STL also has this in the works:
> > for (const x : v) { ... }
>
> There is a difference. In your example, the iteration is still taken on
> the iterator, but my example give you const_iterator -- they are not
> required to be different only by qualification! Even in a standard library,
> they can be unrelated types in debug mode for catch more iterator bugs.
>
> And I assume that's one of the reason why such a syntax is not
> proposed in STL's paper.

For Qt containers, that would be important. The range for calls std::begin()
on the container and the non-const overload of the member begin() will try to
implicitly detach the container. If you don't plan on modifying the container,
you'll want to call the const versions instead which you could do by using

for (auto x : as_const(v))

> > Also, if v is a temporary, will your version be lifetime extended?
>
> Yes. as_const(v) does not put v out of full expression.

When I was trying to rewrite Qt's foreach() macro with the C++11 range for, I
ran into exactly that problem: the lifetime of the temporary was too short.
But I could never prove or disprove whether this was a compiler bug, a mistake
in my macro or required behaviour as per the standard...

--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
PGP/GPG: 0x6EF45358; fingerprint:
E067 918B B660 DBD1 105C 966C 33F5 F005 6EF4 5358

Ville Voutilainen

unread,
Aug 21, 2014, 2:16:54 AM8/21/14
to std-pr...@isocpp.org
On 21 August 2014 06:43, Thiago Macieira <thi...@macieira.org> wrote:
>> > Also, if v is a temporary, will your version be lifetime extended?
>> Yes. as_const(v) does not put v out of full expression.
> When I was trying to rewrite Qt's foreach() macro with the C++11 range for, I
> ran into exactly that problem: the lifetime of the temporary was too short.
> But I could never prove or disprove whether this was a compiler bug, a mistake
> in my macro or required behaviour as per the standard...

Perhaps you ran into this:
http://cplusplus.github.io/EWG/ewg-active.html#120

David Krauss

unread,
Aug 21, 2014, 4:07:21 AM8/21/14
to std-pr...@isocpp.org
This has nothing to do with constness.

It looks to me like a design flaw in Boost. The adaptors are not designed to compose, yet the syntax they chose allows well-formed expressions that fail at runtime. It would have been safer if they’d put the adaptors on the LHS:
boost::adaptors::reversed |
boost::adaptors::uniqued  | vec
Then the library designer chooses between reporting an error because the adaptor was applied to something besides a container, or allowing adaptors to functionally compose before application to the container.

The as_const problem at hand is easily solved by overloading the factory function on rvalues, using return by value.

template< typename type >
type const & as_const( type const & ref )
    { return ref; }
 
template< typename type >
std::enable_if_t< ! std::is_reference_v< type >,
type const > as_const( type && val )
   { return std::move( val ); }


If Boost were to apply this, they would need the adaptor to support both reference-proxy and proper adaptor semantics. It doesn’t sound like too much to ask of them.

Ville Voutilainen

unread,
Aug 21, 2014, 4:52:31 AM8/21/14
to std-pr...@isocpp.org
On 21 August 2014 11:07, David Krauss <pot...@gmail.com> wrote:
>
> On 2014–08–21, at 2:16 PM, Ville Voutilainen <ville.vo...@gmail.com>
> wrote:
>
> On 21 August 2014 06:43, Thiago Macieira <thi...@macieira.org> wrote:
>
> Also, if v is a temporary, will your version be lifetime extended?
>
> Yes. as_const(v) does not put v out of full expression.
>
> When I was trying to rewrite Qt's foreach() macro with the C++11 range for,
> I
> ran into exactly that problem: the lifetime of the temporary was too short.
> But I could never prove or disprove whether this was a compiler bug, a
> mistake
> in my macro or required behaviour as per the standard...
>
>
> Perhaps you ran into this:
> http://cplusplus.github.io/EWG/ewg-active.html#120
>
>
> This has nothing to do with constness.

Nobody said it does.

>
> It looks to me like a design flaw in Boost. The adaptors are not designed to
> compose, yet the syntax they chose allows well-formed expressions that fail

That's news to me.

> The as_const problem at hand is easily solved by overloading the factory
> function on rvalues, using return by value.

That will not help with the lifetime issue.

David Krauss

unread,
Aug 21, 2014, 9:00:39 AM8/21/14
to std-pr...@isocpp.org

On 2014–08–21, at 4:52 PM, Ville Voutilainen <ville.vo...@gmail.com> wrote:

Nobody said it does.

Still worth mentioning.

It looks to me like a design flaw in Boost. The adaptors are not designed to
compose, yet the syntax they chose allows well-formed expressions that fail

That's news to me.

???

It’s just advice to anyone designing a similar facility. If composition is going to be a special feature, then put the operand on the RHS. This particular problem with that Boost library appears to have been there since there were two adaptors to compose. It’s not an interaction with C++11 or anything, and it was avoidable at the time the operator overloads were conceived.

The as_const problem at hand is easily solved by overloading the factory
function on rvalues, using return by value.

That will not help with the lifetime issue.

Sure it does, see the demo. as_const works with lvalues and rvalues alike.

(It’s not a factory function in this case, that was just an editing error. The sentence was originally written to refer to Boost. As for them, the bug linked from the EWG issue shows a similar approach to the lifetime issue, as I described. They only seem to be stuck in choosing between improved efficiency by letting owning and non-owning adaptors be different types, or improved compatibility by letting them be the same type with a runtime switch. Nobody there seems to be asking for a new core language feature.)

Ville Voutilainen

unread,
Aug 21, 2014, 9:20:51 AM8/21/14
to std-pr...@isocpp.org
On 21 August 2014 16:00, David Krauss <pot...@gmail.com> wrote:
>>> It looks to me like a design flaw in Boost. The adaptors are not designed to
>>> compose, yet the syntax they chose allows well-formed expressions that fail
>> That's news to me.
> ???

It's news to me that those adaptors are not designed to compose, yes.

>>> The as_const problem at hand is easily solved by overloading the factory
>>> function on rvalues, using return by value.
>> That will not help with the lifetime issue.
> Sure it does, see the demo. as_const works with lvalues and rvalues alike.

No it doesn't, if you combine as_const with other expressions, as EWG 120
illustrates. The subexpression temporaries are gone by the time the loop is
entered, even if the temporary in the full expression gets a lifetime extension.
Whether that's worth fixing is another matter.

David Krauss

unread,
Aug 21, 2014, 9:34:44 AM8/21/14
to std-pr...@isocpp.org
On 2014–08–21, at 9:20 PM, Ville Voutilainen <ville.vo...@gmail.com> wrote:

It's news to me that those adaptors are not designed to compose, yes.

The adaptor templates themselves do compose, but not by the factory functions implemented by operator overloading. The associativity of the operators is what’s causing a problem here; that is my point.

The as_const problem at hand is easily solved by overloading the factory
function on rvalues, using return by value.
That will not help with the lifetime issue.
Sure it does, see the demo. as_const works with lvalues and rvalues alike.

No it doesn't, if you combine as_const with other expressions, as EWG 120
illustrates. The subexpression temporaries are gone by the time the loop is
entered, even if the temporary in the full expression gets a lifetime extension.
Whether that's worth fixing is another matter.

You can pass as_const to itself with no ill effects, or other functions that return by value.

Taking an rvalue reference parameter and returning some part of it by soon-to-be-dangling reference is a bad idea. Using return by value in such cases is a teachable rule to avoid a lot of headaches. But, we’ve recently had this discussion already.

Scott Prager

unread,
Aug 31, 2014, 11:56:50 PM8/31/14
to std-pr...@isocpp.org
Coming into this conversation late, but...


On Monday, August 18, 2014 11:59:38 PM UTC-4, Zhihao Yuan wrote:

  struct Foo
  {
    T& foo()
    {
      // maybe more useful with as_non_const?
      return const_cast<T&>(as_const(*this).foo());
    }

    T const& foo() const
    {
      // ...
    }
  };

The problem I've always had with using the const version in the non-const overload is 
having to make it so verbose when it does something so simple. I'd much prefer something
like (pseudo-code):

    T& foo() {
      // implicitly un-const the result
      return as_const_apply(&Foo::foo, *this);
    }

because I really only want to write const one time.

Sebastian Redl

unread,
Sep 9, 2014, 6:24:28 PM9/9/14
to std-pr...@isocpp.org
On Saturday, August 16, 2014 10:46:45 PM UTC+2, Andrew Tomazos wrote:
On Sat, Aug 16, 2014 at 1:05 PM, Jonathan Coe <jonath...@gmail.com> wrote:
Sometimes it's desirable to use const methods on a non-const object.

For example?


I used a function like as_const  (except I called it const_) when implementing generic algorithms that called back into user code, when I wanted to ensure that user code couldn't do really stupid stuff like modifying in a predicate:

template <typename It, typename Pred>
bool any(It first, It last, Pred p) {
  for (; first != last; ++first) {
    if (p(as_const(*first))) return true;
  }
  return false;
}

I like the idea that the template implementation stops the predicate from modifying the elements even if the iterators allow mutation.

Sebastian

Sebastian Redl

unread,
Sep 9, 2014, 6:29:48 PM9/9/14
to std-pr...@isocpp.org


On Thursday, August 21, 2014 10:07:21 AM UTC+2, David Krauss wrote:
It looks to me like a design flaw in Boost. The adaptors are not designed to compose, yet the syntax they chose allows well-formed expressions that fail at runtime.

It was a perfectly reasonable decision in C++03, where nobody sane would write down the type of a complicated adapter chain, and so the main use case of such chains was as the argument to an algorithm (where template deduction would take care of the type), since any temporaries were sure to outlive the function call and then they'd be gone anyway.

The interaction with C++11's auto (including the hidden auto in for-range) is what exposed the danger of this design.
 
It would have been safer if they’d put the adaptors on the LHS:
boost::adaptors::reversed |
boost::adaptors::uniqued  | vec

It would also have been completely confusing, given that the syntax was modeled after Unix pipes, which work from left to right.

Sebastian

Matthew Woehlke

unread,
Oct 1, 2014, 2:32:32 PM10/1/14
to std-pr...@isocpp.org
On 2014-08-16 07:05, Jonathan Coe wrote:
> Sometimes it's desirable to use const methods on a non-const object.
> This can be done with const_cast, but const_cast can equally well be used
> to do potentially unsafe things and is often flagged up in code reviews or
> banned outright by coding standards/pre-commit scripts.
>
> I propose the addition of a simple function:
>
> template <typename T>
> std::as_const(T&& t)
> {
> return const_cast<const T&&>(t);
> }
>
> which can be used to perform the safe const-cast operation.

I'd like to allow using 'const' as a unary operator:

A a; auto&& const_a = const a; // const_a has type 'A const&'
for (auto item : const list) {...}
auto bar = Bar{}; foo(const bar);
// etc.

I seem to recall there are cases where this would make sense where it is
not possible to use a function to the same effect. (But I may be wrong;
for one, your version has more '&'s than I remember using at the time...)

In either case, I'm in favor.

--
Matthew

Reply all
Reply to author
Forward
0 new messages