只接受某些参数值的函数(C ++)

时间:2012-02-18 14:25:00

标签: c++ function templates c++11 arguments

让我设置场景..

您可以按照以下特定模式打开文件:

#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 特定值的函数?

假设我想重新实现类似于上面的文件处理类。 不同之处在于我将第二个参数设为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 个答案:

答案 0 :(得分:6)

如果您想要编译时选中的值,可以编写 templates 而不是函数参数:

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 :(得分:1)

不,如果你指定你的函数将使用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.
}

(或接受at但不接受x的函数的等价物。传递无效参数时什么都不做是一种合理的策略,就像返回错误或抛出异常一样(尽管有些人会对此提出异议)。

如何处理它取决于你,但它需要在运行时处理,因为编译器没有所有信息。

答案 2 :(得分:0)

您只能在运行时检查值的有效性。如果违反前提条件,您可以使用assert来停止程序执行。

答案 3 :(得分:0)

没有。如果要限制接受的参数,则需要使用枚举或接受从特定接口继承的对象(取决于您想要的复杂程度)。枚举是解决此问题的常用方法。

关于IOnlyAcceptPrimeNumbers的示例设计不合理。如果你想要实现类似的东西,那么最好提供一个像bool setNumber(int number)这样的类方法,如果数字不是素数,它将返回false。如果你想在costructor中做到这一点,真正的替代方法就是抛出一个异常(这不是很好)。

这个概念是你不能简单地依赖用户只传递参数类型允许的值(正确)子集中的元素。

答案 4 :(得分:0)

虽然比您的要求更具限制性(这限制了特定类型可以容纳的值),但您可以尝试以下方式:

// 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本身可以从chars构造对象。所有其他用途均通过复制构造或复制分配完成。

(我想我最初是从Scott Meyers那里学到这种技术的;感谢他/怪我。)