4

我一直在寻找一个示例来展示如何在 C++ 中实现约束(或让我轻松做到这一点的 boost 库),但运气不佳。我能想到的最好的办法是:

#include <boost/function.hpp>
#include <boost/lambda/lambda.hpp>

template<typename T>
class constrained
{
    public:
        constrained(boost::function<bool (T)> constraint, T defaultValue, T value = defaultValue)
        {
            ASSERT(constraint(defaultValue));
            ASSERT(constraint(value));

            this->value = value;
            this->defaultValue = defaultValue;          
            this->constraint = constraint;                      
        }

        void operator=(const T &assignedValue)
        {
            if(constraint(assignedValue))
                value = assignedValue;      
        }   

    private:
        T value;
        T defaultValue;
        boost::function<bool (T)> constraint;
};

int main(int argc, char* argv[])
{
    constrained<int> foo(boost::lambda::_1 > 0 && boost::lambda::_1 < 100, 5, 10);

    foo = 20; // works
    foo = -20; // fails

    return 0;
}

当然,您可能希望约束类提供更多功能。这只是一个起点的想法。

无论如何,我看到的问题是我必须重载 T 定义的所有运算符,以使其真正表现得像 T,而我无法找出它们是什么。现在,我实际上不需要那么多不同类型的约束,所以我可以省略模板并硬编码它们。不过,我想知道是否有一个通用的(或至少更简洁/优雅的)解决方案,或者我的方法是否有任何严重错误。

4

6 回答 6

4

看起来不错的小例子。但一定要实现所有的运算符并处理某种错误的值。

foo = 100; // works
++foo; // should throw an exception or perform an assert

使用boost 运算符来帮助您处理运算符重载。

有一个选项作为模板参数可能会很好:异常或断言。

我会使用这样的课程。最好有一个索引参数来自动检查向量范围并进行断言。

void foo( VectorIndex i );
于 2009-04-15T11:48:37.470 回答
3

您不需要像其他人建议的那样重载所有运算符,尽管这是提供最大控制的方法,因为涉及类型对象的表达式constrained<T>将保持这种类型。

另一种方法是仅重载变异运算符(=、+=、-=、*=、/=、%=、&=、|=、^=、<<=、>>=、pre 和 post ++, pre 和 post --) 并提供用户定义的转换为T

template<typename T>
class constrained {
    ... // As before, plus overloads for all mutating operators
public:
    operator T() const {
        return value;
    }
};

这样,任何涉及constrained<T>对象的表达式(例如x + ywhere xisintyis constrained<int>)都将是 type 的右值T,这通常更方便和高效。不会失去安全性,因为您不需要控制任何涉及constrained<T>对象的表达式的值——您只需要在 aT变为 a时检查约束constrained<T>,即在constrained<T>的构造函数和任何变异运算符中.

于 2009-04-15T13:00:39.730 回答
2

我同意 Mykola Golubyev 的观点,即 boost 运算符会有所帮助。

您应该为正在使用的所有类型定义所需的所有运算符。

如果您使用的任何类型不支持运算符(例如 operator++()),则调用此方法的代码将不会编译,但所有其他用法都会编译。

如果您想对不同类型使用不同的实现,请使用模板专业化。

于 2009-04-15T12:00:51.603 回答
2

您可能对Boost.Constrained_Value感兴趣。去年 12 月对其进行了审查,但它不在最新的 Boost 版本中。IIRC,审查大多是积极的,但仍有待决定。

于 2009-04-15T13:03:53.933 回答
1

我可能只是感到困惑,但是如果您面对的参数不能违反特定约束,那么为它们创建一个类,检查构造函数和赋值运算符中的约束不是最简单的吗?

于 2009-04-15T11:03:19.933 回答
0

Boost 实际上有这样一个库正在讨论中(我不知道它变成了什么)。我还编写了我自己的这种类型版本,其行为略有不同(不太灵活,但更简单)。我在这里写了一个公认的有点偏见的比较:Constrained vs. restricted value types

编辑:显然 Eric 更清楚 boost 的实现发生了什么。

于 2009-04-15T13:07:26.670 回答