4

Continuing my saga, I've realized that I can make overloads of my access functions, using a single std::initializer_list parameter instead:

class array_md
{
    //...
    my_type &        operator []( size_type i )
    { /* Lots of code */ }
    my_type const &  operator []( size_type i ) const
    { /* same Lots of code, with "const" sprinkled in */ }
    my_type &        operator []( std::initializer_list<size_type> i )
    { /* Lots of different code */ }
    my_type const &  operator []( std::initializer_list<size_type> i ) const
    { /* same Lots of different code, with "const" sprinkled in */ }
    //...
};

For my version of at I now have:

class array_md
{
    //...
    template < typename ...Index >
    complicated &        at( Index &&...i )  // (1)
    { /* Lots of code */ }
    template < typename ...Index >
    complicated const &  at( Index &&...i ) const  // (2)
    { /* same Lots of code, with "const" sprinkled in */ }
    my_type &            at( std::initializer_list<size_type> i )  // (3)
    { /* Lots of different code */ }
    my_type const &      at( std::initializer_list<size_type> i ) const  // (4)
    { /* same Lots of different code, with "const" sprinkled in */ }
    //...
};

(Since I can't change the type depending on the amount of entries in an initializer-list, because it's run-time, I fix the return type and throw if the number of entries is wrong.) The code for the new overloads is long, and I have to repeat it for the mutable and const versions, so I'm wondering how to save code. I tried to mess with const_cast:

class array_md
{
    //...
    my_type &        operator []( size_type i );
    my_type const &  operator []( size_type i ) const;
    my_type &        operator []( std::initializer_list<size_type> i )
    {
        return const_cast<my_type &>( const_cast<array_md const
         *>(this)->operator [](i) );
    }
    my_type const &  operator []( std::initializer_list<size_type> i ) const;
    //...
};

Here the third version calls the fourth, using const_cast to get around compiler complaints. (The trespass is OK since I'm stripping const from something I slapped it on to begin with. Don't reverse the dependencies; you may end up calling a mutable member function on a truly const object!) I tried to do the same thing for at, implementing the overload marked (3) with the one marked (4). However, since there are three alternates for at, I got errors related to (2) getting selected instead! Is there something to how I'm passing the std::initializer_list to the inner at call (by value) that doesn't cause an exact match compared to a universal overload? I'm old friends with universal method conflicts.

TL;DR: Example code shows std::initializer_list objects being taken by value in function parameter lists. Is that the compiler's first preference in passing them? Or is it by reference instead? This is important if you need an exact match (to defeat a universal overload).

Community
  • 1
  • 1
CTMacUser
  • 1,996
  • 1
  • 16
  • 27
  • Note: the inner `const_cast` is unnecessary because adding `const` is always allowed, as in `array_md const& me = *this;`. – Matthieu M. Apr 29 '13 at 06:56
  • 1
    @MatthieuM. Actually, it *is* necessary, to drive overload resolution. Otherwise, the non-const operator would just be calling itself recursively ad infinitum. – Angew is no longer proud of SO Apr 29 '13 at 07:02
  • 4
    While the question is well-written, I feel like you missed the crucial step of writing a self-contained simulation of your code (as well as what compiler you're using, plus the exact message you get can help sometimes). Because as it is, I can reproduce [the situation you describe](http://coliru.stacked-crooked.com/view?id=8a8b1112c6a03d84c607af9b8a7e44e8-50d9cfc8a1d350e7409e81e87c2653ba) and it is accepted by GCC 4.8 and behave as expected (i.e. the deleted overloads are not tripped). – Luc Danton Apr 29 '13 at 07:48
  • 1
    @Angew: No, it's not... *if* as I wrote you create a const-reference and call `()` on the const-reference. – Matthieu M. Apr 29 '13 at 09:46
  • @MatthieuM. Right. I misunderstood your const-ref code as just an example of the cast being unnecessary. Of course, calling through `me` will alleviate the need for the `const_cast`. – Angew is no longer proud of SO Apr 29 '13 at 10:06
  • @LucDanton, so the program does run for you, or not? What happens if the overloads aren't `delete`d? – CTMacUser Apr 30 '13 at 04:52
  • @CTMacUser The program runs. I encourage you to edit it, you can see it in action and find the answers to your questions. – Luc Danton Apr 30 '13 at 05:03

1 Answers1

0

Disambiguation cast:

int     f( int );
double  f( double );

template < typename Func, typename ...Args >
void  do_it( Func &&f, Args &&...a );

//...

int  main( int, char *[] )
{
    do_it( (double(*)(double))&f, 5.4 );
    return 0;
}

The code in main is forced to use the second version of f. I thought this capability was unique to the C-cast, but it's under static_cast. So I got something like:

class array_md
{
    //...
    template < typename ...Index >
    complicated &        at( Index &&...i );  // (1)

    template < typename ...Index >
    complicated const &  at( Index &&...i ) const;  // (2)

    my_type &            at( std::initializer_list<size_type> i )  // (3)
    {
        return const_cast<my_type &>(
          (
            const_cast<array_md const *>( this )
            ->*
            static_cast<
              my_type const &
              (array_md::*)
              ( std::initializer_list<size_type> ) const
            >( &array_md::at )
          )( i )
        );
    }

    my_type const &      at( std::initializer_list<size_type> i ) const;  // (4)
    //...
};

(I got my inspiration by answering someone else's post with needing to distinguish function template overloads.) It took a few tries, especially without having to create an intermediate object.

CTMacUser
  • 1,996
  • 1
  • 16
  • 27
  • Since the disambiguation call includes `const`-mode in the member function's signature, I can switch to a bare "`this`" without `const`-qualifying it first. (My example with `operator []` can't because its `const` qualifier is what's fueling the overload resolution.) – CTMacUser Apr 30 '13 at 06:37