Ott
Ott

Reputation: 57

Non-const reference-returning function getting used as r-value over const value-returning function

I have two versions of a function. One returns by value, intended for use as an r-value. The other returns by reference, intended for use as an l-value (to write to the audio_data vector).

float AudioData::sample(unsigned frame, unsigned channel) const
{
    return audio_data[frame * channel_count + channel];
}

float& AudioData::sample(unsigned frame, unsigned channel)
{
    clearSplits(); // Clear out cached information that may be invalidated by this call
    return audio_data[frame * channel_count + channel];
}

While debugging, I've noticed that the reference-returning override is getting used as an r-value:

const float *AudioData::split(unsigned channel)
{
    if (splits[channel] != nullptr) return splits[channel];
    if (channel_count == 1) return data();

    float *split = new float[frame_count];

    for (unsigned i = 0; i < frame_count; ++i)
    {
        split[i] = sample(i, channel); // Calls the reference-returning override
    }

    splits[channel] = split;
    return split;
}
void AudioData::clearSplits() // This gets called while writing to the splits!
{
    for (unsigned i = 0; i < splits.size(); ++i)
    {
        if (splits[i] != nullptr) delete[] splits[i];
        splits[i] = nullptr;
    }
}

Why does the compiler opt to use the non-const l-value override when it's not modifying the return reference and how can I prevent it from doing so?

Upvotes: 1

Views: 62

Answers (1)

Overload resolution does not care at all about the return type of your function. All it cares about are the types of arguments to the call. And in your case, since you are calling sample from split (a function that is not const-qualified), the implicit object argument (this) is not const qualified. As such the non-const overload gets called, and it returns a reference.

An easy solution is to provide another function, by another name, that returns a copy too.

float AudioData::csample(unsigned frame, unsigned channel) const
{
    return sample(fram, channel); // will always call the const overload
}

If you are averse to that, then you can always const_cast your way to calling the correct overload:

split[i] = const_cast<AudioData const *>(this)->sample(i, channel);

Upvotes: 1

Related Questions