Функции, которые будут принимать только определенные значения аргумента (C ++) - PullRequest
2 голосов
/ 18 февраля 2012

Позвольте мне установить сцену ..

Вы можете открывать файлы в определенном режиме, например так:

#include <fstream>

int main(){

    std::fstream myfile;
    myfile.open ("filename", std::ios::app);

    return 0;
}

, что вторым параметром является перечисляемый тип-
, поэтому вы получите сообщение об ошибке компилятора при попытке сделать это:

#include <fstream>

int main(){

    std::fstream myfile;
    myfile.open ("filename", std::ios::lksdjflskdjflksff);

    return 0;
}

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

Вопрос: Есть ли способ написания функций, которые должны принимать определенный тип AND конкретное значение?

Допустим, я хотел заново реализовать класс File Handling, аналогичный приведенному выше.Разница в том, что я делаю второй параметр char вместо перечислимого типа.
Как я могу заставить что-то подобное работать:

#include "MyFileHandler.h"

int main(){

    MyFileHandler myfile1;

    myfile.open ("filename", 'a'); //GOOD: a stands for append
    myfile.open ("filename", 't'); //GOOD: t stands for truncate
    myfile.open ("filename", 'x'); //COMPILER ERROR: openmode can not be the value 'x'

    return 0;
}

Если я выйду за пределы этого, могу ли я заставить компиляторпроверить правильность значений аргументов с помощью функциональных средств?Пример:

void IOnlyAcceptPrimeNumbers(const int & primeNumber);
int function(void);

int main(){

    IOnlyAcceptPrimeNumbers(3);       //GOOD: 3 is prime
    IOnlyAcceptPrimeNumbers(7);       //GOOD: 7 is prime
    IOnlyAcceptPrimeNumbers(10);      //COMPILER ERROR: 10 is not prime
    IOnlyAcceptPrimeNumbers(10+1);    //GOOD: 11 is prime
    IOnlyAcceptPrimeNumbers(1+1+1+1); //COMPILER ERROR: 4 is not prime
    IOnlyAcceptPrimeNumbers(function()); //GOOD: can this somehow be done?


    return 0;
}
void IOnlyAcceptPrimeNumbers(const int & primeNumber){return;}
int function(void){return 7;}

Полагаю, я ясно дал понять, что хочу делать и почему я нахожу это важным.
Есть ли какие-нибудь решения?

Ответы [ 5 ]

6 голосов
/ 18 февраля 2012

Если вы хотите проверить значения во время компиляции, вы можете написать шаблонов вместо аргументов функции:

template <char> void foo(std::string const &);      // no implementation

template <> void foo<'a'>(std::string const & s) { /* ... */ } 
template <> void foo<'b'>(std::string const & s) { /* ... */ }

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

foo<'a'>("hello world");   // OK
foo<'z'>("dlrow olleh");   // Linker error, `foo<'z'>` not defined.

Если вы хотитефактическая ошибка компилятора , а не просто ошибка компоновщика, вы можете добавить static_assert(false) в основной шаблон.

1 голос
/ 18 февраля 2012

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

"Разрешение", используемое компилятором для проверки переданных аргументов, является введите , а не набор возможных значений.

Другими словами, вам нужно использовать перечисления для этого, или перенести проверку во время выполнения, или сделать что-то ужасное, например:

static void processAorT (char typ, char *fileName) { ... }
void processA (char *fileName) { processAorT ('a', fileName); }
void processT (char *fileName) { processAorT ('t', fileName); |

(кстати, я бы не советовал).


Сказав это, я не уверен, что то, что вы предлагаете, является хорошей идеей.

Компилятор может обнаруживать недопустимые константы , но он не будет очень успешным, если параметр, переданный в IOnlyAcceptPrimeNumbers, получен из переменной или, что еще хуже, введен пользователем.

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

В другихслова, эта функция должна начинаться:

void IOnlyAcceptPrimeNumbers (int num) {
    if (!isPrime (num)) return;
    // do something with a prime number.
}

(или эквивалентAlent для вашей функции, которая принимает a и t, но не x).Ничего не предпринимать, когда передаются недопустимые параметры, - это разумная стратегия, так как возвращать ошибку или выдавать исключение (хотя, без сомнения, некоторые будут спорить с этим).

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

0 голосов
/ 22 февраля 2012

Хотя это и более строгие ограничения, чем ваши требования (это ограничивает значения, которые может содержать конкретный тип), вы всегда можете попробовать что-то вроде:

// Vowel.h
#ifndef VOWEL_H_
#define VOWEL_H_

class Vowel
{
public:
    static const Vowel A;
    static const Vowel E;
    static const Vowel I;
    static const Vowel O;
    static const Vowel U;

    char get() const { return value; }

private:
    explicit Vowel(char c);

    char value;
};

#endif  /* VOWEL_H_ */


// Vowel.cpp
#include "Vowel.h"

Vowel::Vowel(char c) : value(c) {}

const Vowel Vowel::A('A');
const Vowel Vowel::E('E');
const Vowel Vowel::I('I');
const Vowel Vowel::O('O');
const Vowel Vowel::U('U');

Поскольку конструктор char является закрытым, только сам Vowel может создавать объектыиз чарс.Все остальные виды использования выполняются путем копирования или назначения копирования.

(я думаю, что я первоначально изучил эту технику от Скотта Мейерса; спасибо ему / обвините меня.)

0 голосов
/ 18 февраля 2012

Нет. Если вы хотите ограничить принятые аргументы, вам нужно использовать перечисления или принимать объект, который наследуется от определенного интерфейса (зависит от того, насколько сложным вы хотите его сделать). Перечисления являются распространенным способом решения этой проблемы.

Пример с IOnlyAcceptPrimeNumbers не очень хорошо разработан. Если вы хотите добиться чего-то подобного, было бы лучше предоставить метод класса, такой как bool setNumber(int number), который будет возвращать false, если число не простое. Если вы хотите сделать это в конструкторе, реальная альтернатива - вызвать исключение (что на самом деле не очень приятно).

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

0 голосов
/ 18 февраля 2012

Вы можете проверить достоверность значения только во время выполнения. Лучшее, что вы можете сделать, это использовать assert, чтобы остановить выполнение программы, если предварительное условие нарушено.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...