В C ++ я могу сбросить указатель функции для оператора? - PullRequest
3 голосов
/ 28 июля 2011

Можно ли в C ++ сбросить указатель функции для оператора?

В частности, я хочу установить оператор-функцию-член [] для использования (или не использования) проверки границ.Я попробовал это без удачи:

Возможно ли это вообще?Если да, может ли кто-нибудь исправить синтаксис?

в MyArrayClass.h:

class MyArrayClass {
public:
    bool CheckArrayBounds;
    float BoundsCheck(int i) {
        if (i<_size) 
            return _data[i]; 
        return TCpx(_INVALID); 
    }
    float NoBoundsCheck(int i) {
        return _data[i]; 
    }
    void UseBoundsCheck(bool State) {
        if (State) {
            float (operator[]) = &MyArrayClass::BoundsCheck;
        } else {
            float (operator[]) = &MyArrayClass::NoBoundsCheck;
        }
        CheckArrayBounds = State;
    }
    float operator[](int i) { return _data[i]; };
};

Ответы [ 5 ]

6 голосов
/ 28 июля 2011

Это невозможно. Говоря семантически, функции-члены не являются указателями на функции (хотя они могут быть реализованы таким образом внутри).

Вы можете выполнить проверку State внутри operator[] () или использовать прокси-объект.

2 голосов
/ 28 июля 2011

Вы не можете изменить оператор для конкретного экземпляра объекта во время выполнения, поскольку функции-члены хранятся отдельно от атрибутов объекта и одинаковы для всех экземпляров объекта. И они хранятся в защищенном от записи сегменте памяти. Или даже может быть встроен компилятором.

Тем не менее вы можете проверить состояние внутри реализации оператора [].

Также вы можете создать атрибут at и заменить оператор.

class MyArrayClass {
public:
    float (MyArrayClass::*at)(int i);
    float BoundsCheck(int i);
    float NoBoundsCheck(int i); {
    void UseBoundsCheck(bool State) {
        at = (State)? &MyArrayClass::BoundsCheck : &MyArrayClass::NoBoundsCheck;
    }
};

Использование:

MyArrayClass a;
a.at( 1 );
1 голос
/ 28 июля 2011

C ++ не подходит для этого, к сожалению. Вам нужна гораздо более мощная система типов.

Обратите внимание, что даже если бы вы могли, , вы, вероятно, замедлили бы свою программу : целочисленное сравнение намного дороже, чем вызов функции через указатель. Тем более, что ваш процессор может выполнять прогнозирование: он начнет запускать код, как если бы он прошел проверку, и если он закончится с ошибкой, он может отказаться от всего, что делал.

Но обратите внимание, ваш компилятор умен. Например, если у нас есть это:

#include <algorithm>
#include <cstdlib>
#include <iostream>
#include <iterator>
#include <vector>

void process(std::vector<int>& data)
{
    for (unsigned i = 0; i < data.size(); ++i)
    {
        int v = data.at(i);
        std::cout << v << std::endl;
    }
}

int main()
{
    try
    {
        std::vector<int> data;
        std::generate_n(std::back_inserter(data), std::rand() + 1, std::rand);

        process(data);
    }
    catch (...)
    {
        std::cerr << "nope.avi" << std::endl;
    }
}

И если мы скомпилируем с g++ -O3, не будет кода проверки границ . Действительно, компилятор пришел к выводу, что проверка внутри at() никогда не пройдет (что затем выдает), поэтому он удалил этот код. Таким образом, вы можете оставить проверки границ, и ваш компилятор все равно может их убрать. Тем не менее, обратите внимание, что в более сложных случаях компилятору будет слишком сложно доказать это, поэтому вы заплатите за это.

Это тот тип оптимизаций, который вы могли бы гарантировать с помощью более выразительной системы типов, но компилятор может выполнить их в любом случае. Я не знаю о MSVC; как правило, он не такой умный, но вы можете проверить.

Лучше всего идти по маршруту std::vector<>: предоставьте непроверенный operator[] и проверенный at(). Пусть пользователь вашего класса решит, нужны ли ему чеки.

1 голос
/ 28 июля 2011

номер

Методы (и операторы, которые являются просто синтаксическим сахаром для методов / функций) определяются во время компиляции, а не во время выполнения.

0 голосов
/ 29 июля 2011

Функции и функции-члены фактически являются постоянными и неизменяемыми, и их невозможно изменить во время выполнения.Тем не менее, существует своего рода промежуточный путь к тому, что вы пытаетесь сделать, и вы почти были в своем опубликованном коде.

Что вы можете сделать, это определить указатель на функцию-член и затем изменитьзначение указателя во время выполнения в зависимости от того, хотите ли вы, чтобы проверка границ была активной или нет.

class MyArrayClass {
public:
    typedef float (MyArrayClass::*AccessFn)(int i);
    AccessFn Access;
    bool CheckArrayBounds;

    float BoundsCheck(int i) {
        if (i<_size)
            return _data[i];
        return TCpx(_INVALID);
    }
    float NoBoundsCheck(int i) {
        return _data[i];
    }

    MyArrayClass() { UseBoundsCheck(false); }

    void UseBoundsCheck(bool State) {
        if (State) {
            Access = &MyArrayClass::BoundsCheck;
        } else {
            Access = &MyArrayClass::NoBoundsCheck;
        }
        CheckArrayBounds = State;
    }
    float operator[](int i) { return (this->*Access)(i); }
};

Это самый близкий способ сделать то, что вы просили, я могу придумать.

...