这是我的代码,在这里你看到类型为Base
的对象,接口函数为Do()
,我总是使用Base
类型的对象(用更多类型模板化)并始终调用{ {1}}界面。因此,对于Do()
和ImplBaseDummy1
,ImplBaseDummy2
的{{1}}的具体实现或f()
的{{1}}的默认实现。到目前为止,所有这些都很有效,我有很多代码可以按预期工作,我不想更改API,但如果你能说服我,我可能会这样做。最近我不得不添加更多类型,如ImplBaseDummy2
我尝试永远不会重复代码,所以我创建了一个类似的结构,希望它将继续工作。问题是如何在定义时强制调用Base
的特定实现,并在缺少特定实现时使用默认实现(来自ImplBaseDummy1
),所有这些都应该在调用接口函数ImplBase
时发生对于f
类型的对象(以某种方式模板化)。
Base
此代码按预期编译和运行。我的问题是关于我想要实现的安全机制,以防忘记在Do()
中写Base
(这是初始意图,如果缺少特定实现,请调用{{1}的默认实现}})。在这种情况下会发生的是#include <iostream>
template <class Impl>
class Base
{
public:
void Do() { f_impl(); }
void f() {std::cout << "Base::f" << std::endl; }
protected:
void f_impl() { static_cast<Impl*>(this)->f(); }
};
class ImplBaseDummy1 : public Base<ImplBaseDummy1>
{
};
class ImplBaseDummy2 : public Base<ImplBaseDummy2>
{
public:
void f() {std::cout << "ImplBaseDummy2::f" << std::endl; }
};
template <class Actual>
class ImplBase : public Base<ImplBase<Actual>>
{
public:
typedef Base<ImplBase<Actual>> Parent;
void f() { static_cast<Actual*>(this)->f(); }
};
class Derived1 : public ImplBase<Derived1>
{
public:
typedef ImplBase<Derived1> Parent;
void f() {std::cout << "Derived1::f" << std::endl; }
};
class Derived2 : public ImplBase<Derived2>
{
public:
typedef ImplBase<Derived2> Parent;
using Parent::Parent::f;
};
int main()
{
Base<ImplBaseDummy1> d01;
d01.Do();
Base<ImplBaseDummy2> d02;
d02.Do();
Base<ImplBase<Derived1>> d1;
d1.Do();
Base<ImplBase<Derived2>> d2;
d2.Do();
return 0;
}
无限期地调用自身直到它崩溃(或在编译using Parent::Parent::f;
优化级别时永不退出)。我想实现它类似于
Derived2
但两个实现都没有编译,任何其他想法如何实现此检查?要强调的是,运行时解决方案不是我想要的,我在任何检查时都会更好,当我遇到问题而不是执行额外的测试来执行对{{{{ 1}} interface。
答案 0 :(得分:2)
一般情况下,无法在编译时证明重新进入。
但可以在运行时检查:
static bool inside = false;
assert(!inside);
inside = true;
// rest of function
inside = false;
return whatever; // if non void
局部变量的析构函数在技术上仍然可以最终调用该函数。这可以通过(可重用的)RAII样式对象部分解决:
class nonreentrant
{
bool& inside;
public:
nonreentrant(bool& inside): inside(inside)
{
assert(!this->inside);
this->inside = true;
}
~nonreentrant()
{
this->inside = false;
}
};
// usage
static bool inside = false;
nonreentrant guard(inside);
// rest of function
不幸的是,这不适用于参数,其析构函数将在之后运行。
从技术上讲,仍然可能有一些移动构造函数涉及返回值可能最终调用该函数。无效功能和返回简单可移动物体的功能应该是万无一失的。
答案 1 :(得分:1)
您的代码执行未定义的行为,因此无法检查它是否正常工作&#34;。该标准对程序的行为没有任何要求;所有行为都是&#34;正确&#34;。
ServletRequestDataBinder binder = new ServletRequestDataBinder(formBean);
binder.registerCustomEditor(Date.class, new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"), true, 10));
binder.registerCustomEditor(Boolean.class, new PropertyEditorSupport() {
@Override
public void setAsText(String text) {
logger.debug("boolean text : " + text);
// setValue(type);
}
});
binder.registerCustomEditor(String[].class, new PropertyEditorSupport() {
@Override
public void setAsText(String text) {
logger.debug("array text : " + text);
// setValue(type);
}
});
不是ImplBase<Derived2>
,因此Derived2
会导致未定义的行为结果。这里的无限循环和你想要发生的一样正确。
我已经解决了类似的问题而没有做出未定义的行为。
static_cast<Derived2*>(this)->f()
现在,template <class Impl>
class Base {
public:
void f_impl() {std::cout << "Base::f" << std::endl; }
};
template <class Actual>
class ImplBase : public Base<ImplBase<Actual>> {
public:
void f() { static_cast<Actual*>(this)->f_impl(); }
};
class Derived1 : public ImplBase<Derived1> {
public:
void f_impl() {std::cout << "Derived1::f" << std::endl; }
};
class Derived2 : public ImplBase<Derived2> {
public:
};
和Derived1 d1; d1.f();
做了我认为你想要的事情; Derived2 d2; d2.f();
已覆盖d1
,而f
使用d2
行为。
我们从实现中拆分接口,允许实现调度独立于接口调度而存在。
答案 2 :(得分:1)
不要使用Parent :: Parent :: f声明。 delcare一个f()显式调用parent :: Parent :: f()。那样可以解决你的问题。
如果您想要额外保护以防止忘记声明,您应该声明具有受保护继承的最终类,如:
class Derived1 : protected ImplBase<Derived1>
{
public:
typedef ImplBase<Derived1> Parent;
void f() {std::cout << "Derived1::f" << std::endl; }
};
class Derived2 : protected ImplBase<Derived2>
{
public:
typedef ImplBase<Derived2> Parent;
// void f() { Parent::Parent::f(); } // uncomment to avoid compile error.
};
生成的错误消息非常清楚,并按名称提及错误的类。
这段代码非常人为。我很难搞清楚这种继承有用的目的,因为只需直接声明Derived1或Derived2对象类型的对象就可以很好地工作,而不需要原始代码中的ImplBase&lt;&gt; :: f()。
这与元编程有关吗?我们非常感兴趣。
这是完整的解决方案,改变了4行代码。
#include <iostream>
template <class Impl>
class Base
{
public:
void Do() { f_impl(); }
void f() { std::cout << "Base::f" << std::endl; }
protected:
void f_impl() { static_cast<Impl*>(this)->f(); }
};
class ImplBaseDummy1 : public Base<ImplBaseDummy1>
{
};
class ImplBaseDummy2 : public Base<ImplBaseDummy2>
{
public:
static void f() { std::cout << "ImplBaseDummy2::f" << std::endl; }
};
template <class Actual>
class ImplBase : public Base<ImplBase<Actual>>
{
public:
typedef Base<ImplBase<Actual>> Parent;
void f() { static_cast<Actual*>(this)->f(); }
};
class Derived1 : protected ImplBase<Derived1>
{
public:
typedef ImplBase<Derived1> Parent;
void f() { std::cout << "Derived1::f" << std::endl; }
};
class Derived2 : protected ImplBase<Derived2>
{
public:
typedef ImplBase<Derived2> Parent;
//using Parent::Parent::f;
//void f() { Parent::Parent::f(); } // uncomment to remove compile error
};
int main()
{
Base<ImplBaseDummy1> d01;
d01.Do();
Base<ImplBaseDummy2> d02;
d02.Do();
Base<ImplBase<Derived1>> d1;
d1.Do();
Base<ImplBase<Derived2>> d2;
d2.Do();
return 0;
}