假设我想编写一个通用类,它维护一个始终保持在两个值之间的整数.像这样的东西:
template<int Lower, int Upper>
class MyInt {
private:
int m_int;
public:
// Constructors, operators...
};
Run Code Online (Sandbox Code Playgroud)
类不变量就是这样Lower <= m_int <= Upper
.当然,MyInt应该具有整数所有的常用操作,比如赋值和算术运算符.如果操作将其置于打破其不变量的状态,MyInt将抛出.但是,在许多情况下,这应该是编译时可检测的.考虑这个示例代码:
int foo = 500;
constexpr int const bar = 500;
MyInt<0,100> a = 15; // OK
MyInt<0,100> b = foo; // Throws at runtime
MyInt<0,100> c = 500; // Compile error?
MyInt<0,100> d = bar; // Compile error?
MyInt<0,100> f = std::integral_constant<int, 500>; // Compile error
Run Code Online (Sandbox Code Playgroud)
因为std::integral_constant
,编写适当的构造函数是直截了当的.但是,它可能编译时,检测a
距离范围,c
以及d
有没有关系?指定的值是编译时已知的文字或constexpr常量.
我已经尝试了SFINAE-ing around和whatnot,但我找不到从值语义到模板参数的方法,即使对于这些情况,我声称这些值显然是编译时常量. …
假设我正在开发一个名为 libModern 的库。该库使用称为 libLegacy 的遗留 C 库作为实现策略。libLegacy 的界面如下所示:
typedef uint32_t LegacyFlags;
struct LegacyFoo {
uint32_t x;
uint32_t y;
LegacyFlags flags;
// more data
};
struct LegacyBar {
LegacyFoo foo;
float a;
// more data
};
void legacy_input(LegacyBar const* s); // Does something with s
void legacy_output(LegacyBar* s); // Stores data in s
Run Code Online (Sandbox Code Playgroud)
出于各种原因,libModern 不应该向用户公开 libLegacy 的类型,其中包括:
处理这种情况的教科书方法是 pimpl 习惯用法:libModern 将提供一个包装类型,该类型内部有一个指向遗留数据的指针。然而,这在这里是不可能的,因为 libModern 无法分配动态内存。一般来说,其目标不是增加大量开销。
因此,libModern 定义了自己的类型,这些类型与遗留类型布局兼容,但具有更好的接口。在此示例中,它使用强标志enum
而不是普通uint32_t
标志:
enum class ModernFlags : …
Run Code Online (Sandbox Code Playgroud) 给定一个类型,名称和默认值的列表,我可以轻松编写一个生成有效c ++代码的工具,该代码声明一个具有每种类型,名称和默认值的成员变量的类.例如,给出列表
(和类名"Baz"),它会生成
class Baz {
int foo = 42;
float bar = 0.1f;
}
Run Code Online (Sandbox Code Playgroud)
如果一个工具可以生成这样一个类,编译器不能为我做这个吗?我正在考虑这些方面的事情(注意:这是伪代码):
template <typename ...MemberTypes> class Baz {
MemberTypes::type... MemberTypes::name... = MemberTypes::default...;
}
Run Code Online (Sandbox Code Playgroud)
上面的类将被创建类似的东西
using MyBaz = Baz<member_type<int, "foo", 42>, member_type<float, "bar", 0.1f>>;
Run Code Online (Sandbox Code Playgroud)
可能出现这种情况的原因:
这可能是不可能的原因:
如果可以的话,怎么办呢?如果不可能,为什么不呢?即将到来的c ++ 17在这方面有什么改变吗?
更新:示例问题:
通常,配置数据存储为字符串的层次结构或某种其他形式的"任何类型".但是,这会导致丑陋的代码(config.get<int>("core.timeout")
)并阻止编译器帮助解决,例如,typos(config.get<int>("core.timeuot")
).
通过使用其真实类型声明每个配置变量,编译器可以检查类型并防止拼写错误.但是,需要使用自定义代码将配置数据读入正确的成员变量.如果添加了新配置开关,则很容易忘记更新此代码.
只需指定所有成员的类型和名称,然后让编译器自动生成类(包括读取配置文件的方法)将会很方便.这是我要求的功能的可能用例.
我有一个小的C ++ 17项目,我想为其设置Travis CI。由于它是C ++ 17,因此需要现代编译器。我选择了gcc-7和clang-6。虽然gcc构建可以很好地编译和链接,但我一生都无法弄清楚如何正确设置clang。看起来它总是使用非常过时的预安装gcc的标准库实现,而不是它自己的。
travis日志显示以下行cmake --build . -- VERBOSE=1
:
/usr/bin/clang-6.0 -I / home / travis / build / myuser / perlin / include -g -std = gnu ++ 1z -o CMakeFiles / perlin.dir / main.cpp.o -c / home / travis /build/myuser/perlin/main.cpp
在/usr/bin/../lib/gcc/x86_64-linux-gnu/4.9/../../../../include/c++/4.9/algorithm包含的文件中:62:在/usr/bin/../lib/gcc/x86_64-linux-gnu/4.9/../../../../include/c++/4.9/bits/stl_algo.h:66:位于/usr/bin/../lib/gcc/x86_64-linux-gnu/4.9/../../../../include/c++/4.9/random:38:在/ usr / bin包含的文件中/../lib/gcc/x86_64-linux-gnu/4.9/../../../../include/c++/4.9/cmath:44:/ home / travis / build / myuser / perlin / include /math.h:48:28:错误:名称空间“ std”中没有名为“ is_arithmetic_v”的模板;您是说“ is_arithmetic”吗?
同样,它在gcc上构建良好。我还仔细检查了是否包括所有必需的标头。
我的.travis.yml看起来像这样:
language: cpp
dist: trusty
matrix:
include:
- os: linux
addons:
apt:
sources:
- ubuntu-toolchain-r-test …
Run Code Online (Sandbox Code Playgroud)