3

This is a retelling of my previous post, since I changed the question (so it probably didn't get flagged as a new question and was missed). I'll hopefully trim it down too.

I had functions like:

#include <cstddef>
#include <type_traits>

template < typename E, typename T >
inline constexpr
auto  checked_slice( E &&, T &&t ) noexcept -> T &&
{ return static_cast<T &&>(t); }

template < typename E, typename T, std::size_t N, typename U, typename ...V >
inline constexpr
auto  checked_slice( E &&e, T (&t)[N], U &&u, V &&...v )
 -> typename remove_some_extents<T, sizeof...(V)>::type &
{
    typedef typename std::remove_reference<U>::type                 u_type;
    typedef typename std::common_type<u_type, std::size_t>::type  cmp_type;

    return ( u < u_type{} ) || ( static_cast<cmp_type>(u) >=
     static_cast<cmp_type>(N) ) ? throw e : checked_slice( static_cast<E &&>(e),
     t[static_cast<U &&>( u )], static_cast<V &&>(v)... );
}

where remove_some_extents is a custom class template that's like calling the std::remove_extent meta-function a given number of times.

When I tried running the program, I got a bunch of errors like: "invalid initialization of reference of type Whatever(&)[X][Y] from expression of type Whatever(*)[Y]" (or Whatever(&)[Z] from Whatever*). My workaround was to convert the conditional expression to an if-else pair (and removing the constexpr).

I'm trying to figure out what's wrong, so I'm poking around the section about the conditional operator in the C++ (2011) standard. That's section 5.16. When one of the two possible actions is a throw command (or is otherwise a void expression), then the conditional has the type of the other expression, but the standard conversions, including array-to-pointer, is applied to that other expression. (This is in paragraph 2.) I think that's what's messing me up. Is there any way around it? I thought returning an array reference suppresses the a-to-p conversion. Why does it work when made into an if/else?

Community
  • 1
  • 1
CTMacUser
  • 1,996
  • 1
  • 16
  • 27

1 Answers1

4

Your analysis is correct. I suspect that the non-void operand is 'decayed' (that is, the usual conversions are performed) in such a situation so as to mimic what happens when the two operands differ in types -- in the latter case more often than not the whole conditional expression is a prvalue.

One situation in which we know for sure both value category and type of a conditional expression is when the two operands are exact matches, so we can use that to our advantage:

cond ? (throw e, t) : t

will be an lvalue of array reference type. (Of course the last operand doesn't have to be literally t -- you can plug your recursive call here just fine.)

You did not encounter any such hurdle when using if/else because as a statement the language does not have to specify a common type and value category for it.

Luc Danton
  • 34,649
  • 6
  • 70
  • 114
  • From testing at a compiler web page, you don't need the parentheses! The syntax for `?:` is `LOGICAL_OR_EXPR ? EXPR : ASSIGN_EXPR`. The 3rd section can be anything except for the comma operator. The 2nd section does *not* have that restriction! (The 1st section is anything with higher precedence than `?:`.) – CTMacUser Apr 27 '13 at 10:56
  • Parentheses are rarely for the benefit of the compiler. – Luc Danton Apr 27 '13 at 11:08
  • The answer worked. You can see [the code](https://github.com/CTMacUser/ArrayMD/blob/master/include/boost/utility/slice.hpp) with commit SHA 66ca088332dd1e57fdaf9e90749105224c301afb, lines 136 through 159. – CTMacUser Apr 27 '13 at 12:27