遗憾的是,UFCS没有进入C++ 17,这给我带来了一个反复出现的问题:有时我想使用方法调用语法给类型提供额外的功能(不需要编写全局函数).在处理monad时,这尤其方便.
我看到两个选项:一个是继承,另一个是封装.因为您无法安全地从STL容器继承,所以会留下封装.例如,我想扩展std::optional,所以我写道:
template <typename T>
struct myoption {
// Some functionality
private:
std::optional<T> impl;
};
Run Code Online (Sandbox Code Playgroud)
我的问题是,每次我想要这样做时,我基本上都必须编写push_back原始类型具有的所有构造函数(以及可以与原始类型一起使用的所需方法,如向量).即使是更简单的容器,也可选有9个构造函数.使用继承时,我可以简单地"继承"超类的方法和构造函数.有没有办法使用封装更容易?
我希望以下代码能够工作:
template <typename Self>
struct foo_base {
auto get(typename Self::type n) { return n; }
};
template <typename T>
struct foo : public foo_base<foo<T>> {
using type = T;
};
Run Code Online (Sandbox Code Playgroud)
问题当然是首先实例化基础,因此您不能引用派生成员类型.我需要在这里进行某种懒惰的评估.
我已经尝试制作功能模板并在其上安装了SFINAE,类似于:
template <typename Self>
struct foo_base {
template <typename T, typename = std::enable_if_t<std::is_same_v<T, typename Self::type>>>
auto get(T n) { return n; }
};
Run Code Online (Sandbox Code Playgroud)
但它似乎不会影响订单.有任何想法吗?
编辑:
解决方案的限制:
struct foo : foo_base<foo<T>, T>或变种.我希望能够在类型的ID上创建switch语句.我找到了一种机制,可以为不同类型提供唯一的ID.这很简单:
template <typename T>
struct type {
static void id() { }
};
template <typename T>
constexpr const size_t type_id() {
return reinterpret_cast<size_t>(&type<T>::id);
}
Run Code Online (Sandbox Code Playgroud)
我认为这将评估为一个常量,我可以用作开关的情况.但是当我执行以下操作时,我得到一个错误,即case表达式不是常量:
int main(void) {
size_t a = type_id<int>();
switch (a) {
case type_id<int>():
break;
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
为什么不是常数?我怎么能达到这个效果?
编辑:
如果没有reinterpret_cast,我可以做这样的事情吗?
是否有可能解析一个表达式(没有歧义),它可以包含二进制前缀,二进制中缀和二进制后缀运算符(让我们假设所有符号都不同),它们之间具有优先权?例如:
a = 2 3 post+
b = pre+ 2 3*4
Run Code Online (Sandbox Code Playgroud)
然后a将等于5因为=优先级低于后缀post+运算符并且b将是14.我知道你可以用运算符优先解析或分流码解析中缀表示法,但这个问题对我来说似乎要复杂得多.
编辑:
允许括号,并且运算符的前/后变体具有与中缀相同的优先级.
我想推出一个手写算法.
EDIT2:
按优先顺序,我指的是消费多少.例如:
a = 2 3 post+
Run Code Online (Sandbox Code Playgroud)
可能导致这些AST-s:
'=' has higher precedence than 'post+':
post+
/ \
= 3
/ \
a 2
'post+' has higher precedence than '=':
=
/ \
a post+
/ \
2 3
Run Code Online (Sandbox Code Playgroud)
(第二个是我在这种情况下需要的).我不能真正使用现有的解析器生成器或固定语法来操作数,因为运算符是动态加载的.
parsing operator-precedence infix-operator prefix-operator postfix-operator
我正在用 C++ 编写一个编译器,与任何编译器一样,它需要大量的模式匹配和动态转换。在 Rust、Haskell 和 OCaml 等语言中,我可以轻松地破坏类型,例如:
match node {
Binary{ left, right, operator } => { .. }
_ => { .. }
}
Run Code Online (Sandbox Code Playgroud)
在 C++ 中我能做的最好的事情是:
if (auto bin = dynamic_cast<Binary*>(node)) { ... }
else if (...) { ... }
Run Code Online (Sandbox Code Playgroud)
如果您将智能指针引入场景中,这确实是有限且丑陋的。例如,如果我需要匹配 2 个东西:
bool matched = false;
if (auto m1 = dynamic_cast<Foo*>(a)) {
if (auto m2 = dynamic_cast<Bar*>(b)) {
matched = true;
}
}
if (!matched) {
// This is because C++ does not allow you to declare …Run Code Online (Sandbox Code Playgroud) 我有一个这样的课:
template <unsigned int A, unsigned int B>
class Foo { ... };
Run Code Online (Sandbox Code Playgroud)
Foo需要一个名为bar()的方法,但我需要专门化它.对于一个案例,当A == B时,我希望它做一件事,否则就是其他事情.如果不在函数中编写if语句,我可以这样做吗?喜欢:
Foo<A, A>::bar() { ... } and Foo<A, B>::bar() { ... }
Run Code Online (Sandbox Code Playgroud) 以下是有效的Rust:
enum Foo {
One(i32, i32, i32),
Two { x: i32, y: i32 },
}
fn main() {
let x: [Foo; 2] = [Foo::One(1, 2, 3), Foo::Two { x: 1, y: 2 }];
}
Run Code Online (Sandbox Code Playgroud)
Rust如何存储这个?第一个元素是12个字节,而第二个元素是8(我想在开头加上一个标记字节).它是否只存储对数组中元素的引用?
我读过Rust在使用Hindley-Milner时具有很好的类型推断。Rust还具有可变变量,并且当HM算法具有可变性时,AFAIK必须存在一些约束,因为它可能过于笼统。如下代码:
let mut a;
a = 3;
a = 2.5;
Run Code Online (Sandbox Code Playgroud)
不进行编译,因为在第二行推断出整数,并且不能将浮点值分配给整数变量。因此,我猜测对于简单变量,一旦推断出非泛型类型,该变量就会成为单类型并且无法再进行泛化。
但是,像Vec这样的模板呢?例如此代码:
let mut v;
v = Vec::new();
v.push(3);
v.push(2.3);
Run Code Online (Sandbox Code Playgroud)
这再次失败,但对于最后一行再次失败。这意味着第二行部分推断了类型(Vec),而第三行推断了容器类型。
怎么了 有我不知道的诸如价值限制之类的东西吗?还是我使事情复杂化了,Rust有更严格的规则(就像根本没有泛化一样)?