- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
考虑以下代码,其中 Writer_I
充当接口(interface)。其他符合以正确形式编写元素类型的约定的类可以从它派生。这里,printf 和streams 被选为策略,Calculator
作为用户。
该接口(interface)以某种方式存储在 Calculator
和 write_i
隐藏模板的所有丑陋细节,使类成员函数保持干净。大多数事情在编译时仍然是已知的,并且可以内联。
我知道这是基于虚拟 + 派生的多态性的经典案例,其中可以将非模板化接口(interface)存储在 Calculator
中。和 write
成员函数被调用。但是在编译时知道类型,并且仍然将分辨率推迟到运行时似乎很糟糕。它暗示某些运行时值会影响所选的写入方法,而事实并非如此。
一种方法是制作 Calculator
模板并将其实现保存在 cpp 文件中,并将 cpp 文件包含在测试中。这太恶心了。 Calculator
的每一种方法会有没用的template <>
在顶端。它只被实例化一次。 (如果你可以测试两次,但是如果制作 Calculator
模板的唯一原因是测试,我会说测试太侵入性了。)
我看到了谈话https://www.youtube.com/watch?v=mU_n_ohIHQk (元多态 - Jonathan Boccara - Meeting C++ 2020 开幕主题演讲)https://meetingcpp.com/mcpp/slides/2020/meta_polymorphism_pdf3243.pdf
它展示了一种具有 std::any
的技术(将存储 Writer_I 实例引用)+ lambda(包含实际的 Impl 类型)+ 函数指针(可以稍后调用)。幻灯片 79-83。我试过但很快就卡住了:How to have a function pointer to a generic lambda?
在出于好奇而进行了所有这些徒劳的尝试之后,我的解决方案是使用 iterator pattern并释放 Calculator
从“写作”的责任。 Calculator
应该只是计算数据,而不是写入数据。这样就解决了问题!调用者通过运行 iterator++
获取数据并以任何喜欢的方式编写它。或者甚至可能不写,而直接测试数字。 Calculator
仍然是非模板,因此在 cpp 文件中。
但是,如果有任何方法可以实现我对当前设计的预期,我会很高兴看到它。
我知道有一些矛盾的约束,比如使用类型删除,它可能在内部使用 virtual,但在 Stack Overflow 上允许好奇心,对 (; ?
https://godbolt.org/z/W74833
编辑:澄清一下,这里的用户类是 Calculator
这不应该是一个模板。所有作者都可以保留在标题中,无需隐藏。对于CRTP,main
中实际需要了解每个编写器实现的作用。
#include <any>
#include <iostream>
#include <type_traits>
#include <utility>
enum class Elem {
HEADER,
FOOTER,
};
template <typename Impl> class Writer_I {
public:
template <Elem elemtype, typename... T> decltype(auto) write(T &&...args) {
return static_cast<Impl *>(this)->template write<elemtype>(
std::forward<T>(args)...);
}
virtual ~Writer_I() {}
};
class Streams : public Writer_I<Streams> {
public:
template <Elem elemtype, std::enable_if_t<elemtype == Elem::HEADER, int> = 0>
void write(int a) {
std::cout << a << std::endl;
}
template <Elem elemtype, std::enable_if_t<elemtype == Elem::FOOTER, int> = 0>
void write(float a) {
std::cout << "\n-------\n" << a << std::endl;
}
};
class Printf : public Writer_I<Printf>{
public:
template <Elem elemtype, std::enable_if_t<elemtype == Elem::HEADER, int> = 0>
void write(int a) {
std::printf("%d\n", a);
}
template <Elem elemtype, std::enable_if_t<elemtype == Elem::FOOTER, int> = 0>
void write(float a) {
std::printf("\n--------\n%f\n", a);
}
};
/* Restrictions being that member functions header and footer
remain in cpp files. And callers of Calculator's constructor
can specify alternative implementations. */
class Calculator {
std::any writer;
public:
template <typename Impl>
Calculator(Writer_I<Impl> &writer) : writer(writer) {}
template <Elem elemtype, typename... T> void write_i(T &&...args) {
// MAGIC_CAST ----------------------↓
auto a = std::any_cast<Writer_I<Printf>>(writer);
a.write<elemtype>(std::forward<T>(args)...);
}
void header() {
for (int i = 0; i < 10; i++) {
write_i<Elem::HEADER>(i);
}
}
void footer() {
write_i<Elem::FOOTER>(-100.0f);
}
};
int main() {
Streams streams;
// Calculator calc_s(streams); // throws bad_cast.
// calc_s.header();
// calc_s.footer();
Printf printf_;
Calculator calc_p(printf_);
calc_p.header();
calc_p.footer();
return 0;
}
最佳答案
您的设计约束是 Calculator
不需要是模板,并且必须使用编写器进行初始化。
这意味着它与编写器的接口(interface)必须是动态的。它可以通过虚拟接口(interface)类,通过存储函数指针,或稍后传递指针,或类似的方式。
由于您不希望修复 writer 的多态接口(interface),因此排除了虚拟接口(interface)。
现在,我们可以手动执行此操作。
void header() {
for (int i = 0; i < 10; i++) {
write_i<Elem::HEADER>(i);
}
}
void footer() {
write_i<Elem::FOOTER>(-100.0f);
}
这些是我们需要键入删除的调用。我们需要输入删除到他们的签名,并记住以后如何做。
template<class T>
struct tag_t { using type=T; };
template<class T>
constexpr tag_t<T> tag = {};
template<class Sig, class Any=std::any>
struct any_type_erase;
template<class R, class...Args, class Any>
struct any_type_erase<R(Args...)> {
std::function<R(Any&, Args&&...args)> operation;
any_type_erase() = default;
any_type_erase(any_type_erase const&) = default;
any_type_erase(any_type_erase &&) = default;
any_type_erase& operator=(any_type_erase const&) = default;
any_type_erase& operator=(any_type_erase &&) = default;
template<class T, class F>
any_type_erase(tag_t<T>, F&& f) {
operation = [f=std::forward<F>(f)](Any& object, Args&&...args)->R {
return f(*std::any_cast<T*>(&object), std::forward<Args>(args)...);
};
}
R operator()(Any& any, Args...args)const {
return operation(any, std::forward<Args>(args)...);
}
};
any_type_erase
是有点 helper 做装箱的操作。对于
const
操作,传入
std::any const
作为第二个论点。
std::any writer;
any_type_erase<void(int)> print_header;
any_type_erase<void(float)> print_footer;
template<class T>
static auto invoke_writer() {
return [](auto& writer, auto&&..args) {
writer.write<T>(decltype(args)(args)...);
};
}
template<typename Impl>
Calculator(Writer_I<Impl>& writer) :
writer(writer),
print_header( tag<Writer_I<Impl>>, invoke_writer<Elem::HEADER>() ),
print_footer( tag<Writer_I<Impl>>, invoke_writer<Elem::FOOTER>() )
{}
void header() {
for (int i = 0; i < 10; i++) {
print_header( writer, i );
}
}
void footer() {
print_footer( writer, -100.0f );
}
关于c++ - 策略类设计,但没有使整个用户类成为模板,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65628159/
我正在学习 Go,但我无法在任何地方找到这个答案。 Web开发中的文件扩展名是否有任何官方标准?我见过多种约定,例如 .tmpl 和 .gtpl,这是什么?谢谢。 最佳答案 没有固定的标准,但有一些相
关闭。这个问题是opinion-based .它目前不接受答案。 想改善这个问题吗?更新问题,以便可以通过 editing this post 用事实和引文回答问题. 8 年前关闭。 Improve
假设我有两个类(class) Widget ^ | Window 我还有另一个类应用程序: 定义如下 class Application { public: ... private:
按照目前的情况,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visit the
我多年来一直在使用 MySQL,直到去年左右,主要是在较小的项目上。我不确定是语言的性质还是我缺乏真正的教程让我不确定我正在写的东西是否是优化目的和扩展目的的正确方法。 虽然自学 PHP,但我对自己和
我已经多次读到 EJB 是重量级的……但昨晚我正在浏览关于 EJB 的 Java EE 6 教程,它们似乎只是普通的 Java 对象,除了它们可以有像 Stateless 或 Singletons 这
如何使此 SimpleModal 脚本在页面加载时加载而不是单击按钮?谢谢=) Demo 基本模态对话框 对于此演示,SimpleModal 使用此“隐藏”数据作为其内容。您还可以使用标准 HTML
这是 Haskell 中的代码: class Fooable a where foo :: a -> a instance Fooable (a, a) where foo = ...
是否有推荐的方法来测试 Actor 是否使用 be 正确改变了其行为?我更喜欢使用 FSM 的原因之一是因为我可以轻松验证 Actor 是否已更改其行为。我不知道在使用 become/unbecome
我正在构建一个位于“php my admin”中的表,我是在第一次点击其中一个“th”它的 asc 时这样做的,现在我试图在第二次点击时制作 desc ..有什么想法吗? 阿姆..很多我不记得了抱歉.
考虑以下网页。 我在 Firefox 中打开此页面,打开 JS 控制台并键入以下内容。 > document.getElementById(
如何让自己成为 postgresql 的 super 用户? 我一直在尝试创建数据库,但我不断收到以下错误: createdb: database creation failed: ERROR: pe
Query没有太大帮助。 如前所述here , PostgreSQL 是 ORDBMS。 here ,它解释了 PostgreSQL 是 RDBMS。 PostgreSQL 是一个 ORDBMS 是什
我已经看到,当在导航元素中使用的链接中垂直/水平居中文本时,将链接设置为 flex 容器会很有用。我没有意识到链接文本实际上可以是一个(单个) flex 元素。我可以看到链接中的 span 元素可以是
我见过很多说明如何找到给定集合的子集的示例,但是您如何将一个集合设为另一个集合的子集?所以集合 B 是集合 A 的子集,这将如何实现?我目前正在使用递归性质的方案,但是这本书只展示了如何列出子集而不是
有些程序会根据其标准输出是否为 tty 来更改其输出。因此,如果您将它们放入管道或重定向它们,输出将与您的 shell 中的不同。这是一个例子: $ touch a b c # when runnin
我正处于项目的开始阶段,到目前为止我一直在使用默认的 MySQL 数据库。 对了,默认的数据库有名字吗? 我的问题是如何在不删除当前表和创建新表的情况下将现有表更改为 utf-8 和 InnoDB。是
我正在尝试编写一个过滤器来包装数据以遵循 JSON API spec到目前为止,它适用于我直接返回 ActionResult 的所有情况,例如 ComplexTypeJSON。我试图让它在像 Comp
我在 Storyboard 上创建了一个带有一个 UITextField 的自定义 UIViewController。在 viewDidLoad 上,我将 UITextFIeld 设置为 become
我已经看到关于 valueless_by_exception 方法的 cppreference 的以下注释: A variant may become valueless in the followi
我是一名优秀的程序员,十分优秀!