C++ 确保在编译时调用方法

C++ 确保在编译时调用方法,c++,c++11,C++,C++11,如何确保在编译时调用特定的方法 例如,假设我有一个具有2种方法的对象: struct Foo { ... func1(...); ... func2(...); }; 我想确保在调用func2之前调用func1,即: int main() { Foo f; ... f.func1(...); f.func2(...); f.func2(...); // and so on } 但如果我这样做,我想生成一个编译错误: int main() { F

如何确保在编译时调用特定的方法

例如,假设我有一个具有2种方法的对象:

struct Foo
{
   ... func1(...);
   ... func2(...);
};
我想确保在调用func2之前调用func1,即:

int main()
{
   Foo f;
   ...
   f.func1(...);
   f.func2(...);
   f.func2(...); // and so on
}
但如果我这样做,我想生成一个编译错误:

int main()
{
   Foo f;
   ...
   f.func2(...);  // generate a compile error due the fact that func1 must be called first
   f.func1(...);
   f.func2(...); // and so on
}

在编译时没有真正的方法来强制执行这一点。这是对象本身需要通过运行时检查强制执行的内容

在编译时没有真正的方法强制执行。这是对象本身需要通过运行时检查强制执行的内容

虽然您很好奇为什么要这样做,但一般注意的是,您必须向用户公开一个不能错误使用的界面。胆量私人化:

struct Foo
{
public:
    void callme()
    {
        func1();
        func2();
    }

private:
    ... func1(...);
    ... func2(...);


};

int main()
{
    Foo f;
    f.callme();
}
如果需要强制执行对象的一次性初始化,请在构造函数中执行:

struct Foo
{
public:
    Foo()
    {
        func1();
    }

    func2(...);
private:
    ... func1(...);
};

int main()
{
    Foo f; // func1() called automagically
    f.func2();
}
class Foo
{
private:
    void func1(), func2();
    friend class FooUser;
};

class FooUser
{
public:
    explicit Proxy(FooUser& f) : f_(f) {
        f.func1();
    }

    void func2() {
        f_.func2();
    }
};

在设计类接口时,您必须始终考虑最糟糕的事情:用户从不阅读文档,用户总是忘记调用
foo.initialize()
,用户总是忘记释放内存和内存泄漏,等等,一般注意事项是,您必须向用户公开一个不能错误使用的界面。胆量私人化:

struct Foo
{
public:
    void callme()
    {
        func1();
        func2();
    }

private:
    ... func1(...);
    ... func2(...);


};

int main()
{
    Foo f;
    f.callme();
}
如果需要强制执行对象的一次性初始化,请在构造函数中执行:

struct Foo
{
public:
    Foo()
    {
        func1();
    }

    func2(...);
private:
    ... func1(...);
};

int main()
{
    Foo f; // func1() called automagically
    f.func2();
}
class Foo
{
private:
    void func1(), func2();
    friend class FooUser;
};

class FooUser
{
public:
    explicit Proxy(FooUser& f) : f_(f) {
        f.func1();
    }

    void func2() {
        f_.func2();
    }
};

在设计类接口时,您必须始终考虑最糟糕的事情:用户从不阅读文档,用户总是忘记调用
foo.initialize()
,用户总是忘记释放内存和内存泄漏,等等。

想到的一种方法是让func1()返回一个对象,该对象充当func2()的代理:

另一种方法(也是我最喜欢的方法)是让func1()成为构造函数,或者使用另一个类,该类在其构造函数中调用func1():

struct Foo
{
public:
    Foo()
    {
        func1();
    }

    func2(...);
private:
    ... func1(...);
};

int main()
{
    Foo f; // func1() called automagically
    f.func2();
}
class Foo
{
private:
    void func1(), func2();
    friend class FooUser;
};

class FooUser
{
public:
    explicit Proxy(FooUser& f) : f_(f) {
        f.func1();
    }

    void func2() {
        f_.func2();
    }
};

我想到的一种方法是让func1()返回一个对象,该对象的行为类似于func2()的代理:

另一种方法(也是我最喜欢的方法)是让func1()成为构造函数,或者使用另一个类,该类在其构造函数中调用func1():

struct Foo
{
public:
    Foo()
    {
        func1();
    }

    func2(...);
private:
    ... func1(...);
};

int main()
{
    Foo f; // func1() called automagically
    f.func2();
}
class Foo
{
private:
    void func1(), func2();
    friend class FooUser;
};

class FooUser
{
public:
    explicit Proxy(FooUser& f) : f_(f) {
        f.func1();
    }

    void func2() {
        f_.func2();
    }
};

编译器不能强制执行函数调用的顺序,因为通常只能在运行时确定。但它可以在使用对象之前强制对象初始化。因此,获得编译时检查的最佳方法是执行
func1
在构造函数中所做的任何操作-要么是
Foo
的构造函数的一部分,要么是为了调用
func2

而需要创建的帮助对象,编译器无法强制执行函数调用的顺序,因为通常只能在运行时确定。但它可以在使用对象之前强制对象初始化。因此,获得编译时检查的最佳方法是执行
func1
在构造函数中所做的任何操作-要么是
Foo
的构造函数的一部分,要么是为了调用
func2

而需要创建的帮助对象,我确实不建议这样做,但如果出于调试目的需要它,您可以尝试以下操作。 将每个调用func1()更改为

和func2()到


因此,如果在文本的上面提到func2(),然后是func1,则会收到编译时错误。这并不意味着它将在执行过程中被提前调用。

我确实不建议这样做,但如果您出于调试目的需要它,您可以尝试以下操作。 将每个调用func1()更改为

和func2()到


因此,如果在文本的上面提到func2(),然后是func1,则会收到编译时错误。这并不意味着它将在执行过程中被提前调用。

您可以尝试使用
constepr
,但在这种情况下它不会工作。(不保证在任何情况下都能工作)。我想这只能通过代码分析器来完成。编译器本身只能警告您一个未使用的函数-AFAIK。如果您需要这样做,您可能应该继续考虑更好的类设计。这是一个压缩耦合反模式:您可以尝试使用
constexpr
,但在这种情况下它不会工作。(不保证在任何情况下都能工作)。我想这只能通过代码分析器来完成。编译器本身只能警告您一个未使用的函数-AFAIK。如果您需要这样做,您可能应该继续考虑更好的类设计。这是一个纯粹的耦合反模式:我还要提到,您可以使用
std::forward
将参数从
callme
传递到
func1…funcn
。只要有足够的决心,你就可以从它应该使用的函数列表中自动创建
callme
的签名,使其更加透明。你问过我为什么要这样做。看看这篇文章()。在这里,当您使用模板执行策略模式(不确定它是否真的是一个策略模式)时,您初始化对象,而不是通过构造函数,而是调用init函数。我想确保用户已经这样做了,但是在编译时。@TomásBadan我认为在本例中,
init()
方法没有任何必要。事实上,它们可以被构造函数替换。因此,无论以前在哪里和谁调用
init()
,现在都只需将参数传递给构造函数或工厂(工厂将参数传递给构造函数)。@Drop如文中所述:“(…)writer类实现了…init方法…来初始化对象…因为我们将它们定义为记录器的基类,而这个类不能拥有所有可能的超类的“构造函数”。(…)。因此,即使将init参数传递给基构造函数,问题是,用户必须调用init来初始化基对象,否则,它们很可能由默认构造函数初始化。@TomásBadan我们确实可以在基类中使用任何参数组合的构造函数(可变模板魔术)。还有其他解决办法。顺便说一下,本文中描述的模式与“策略”没有任何共同之处。它被称为。但是这个问题