c++ - C++ if (false) 条件评估?
问题描述
我正在学习 C++ 中的模板,而我的 C++ 术语集有些有限,所以我无法用谷歌搜索这个问题。
我正在尝试实现dict
基于std::unordered_map
. 我的目标是能够以dict
如下方式实例化类:
dict<std::string, long> d; // OR
dict<std::string, std::set<std::string>> d; // OR
dict<std::string, std::map<char, float>> d; // OR
dict<std::string, std::vector<std::string>> d; // OR
dict<std::string, std::vector<double>> d;
所以这是代码,我正在使用:
实用程序.h
#include <fstream>
#include <unordered_map>
#include <set>
#include <vector>
#include <algorithm>
#include <type_traits>
// for bravity
using namespace std;
// to check for stl vector
// slightly modified version of: https://stackoverflow.com/a/31105859
namespace is_container {
template <typename T> struct stl_vector : false_type{};
template <typename T> struct stl_vector<std::vector<T>> : true_type{};
}
namespace StringOps {
// generaic function to split based on many delimiters:
// source: https://stackoverflow.com/a/9676623
vector<string> split(const string& str, const string& delimiters = " ,") {
vector<string> v;
unsigned start = 0;
auto pos = str.find_first_of(delimiters, start);
while(pos != string::npos) {
if(pos != start) // ignore empty tokens
v.emplace_back(str, start, pos - start);
start = pos + 1;
pos = str.find_first_of(delimiters, start);
}
if(start < str.length()) // ignore trailing delimiter
v.emplace_back(str, start, str.length() - start); // add what's left of the string
return v;
}
}
template<class Key, template <class...> class Value, typename T, class = void>
class dict {
public:
Value<T> t;
};
template<class Key, template <class...> class Value, typename T> // detect container types with ::iterator
class dict<Key, Value, T, void_t<typename Value<T>::iterator>> : true_type {
private:
unordered_map<Key, Value<T>> content;
bool is_vector = false;
string line;
unordered_map<Key, Value<T>> load(ifstream& file) {
while (getline(file, line)) {
if (!line.empty()) {
// remove trailling \n if exists
if (line[line.length()-1] == '\n')
line.erase(line.length() - 1);
vector<string> tokens = StringOps::split(line);
Value<T> result;
(tokens[i]));
if (is_vector) {
for (unsigned i = 1; i < tokens.size(); i++) {
result.emplace_back(static_cast<T>(tokens[i]));
}
}
if(false) { // should never be looked into
auto it = result.cend();
for (unsigned i = 1; i < tokens.size(); i++) {
result.emplace_hint(it, static_cast<T>(tokens[i]));
}
}
content[static_cast<Key>(tokens[0])] = result;
}
}
return content;
}
public:
constexpr Value<T>& operator[](Key k) {
return content[k];
}
dict(const string& path) {
// detect vector type
if(is_container::stl_vector<decay_t<Value<T>>>::value)
is_vector = true;
ifstream file(path);
content = load(file);
}
constexpr unsigned size() {
return content.size();
}
};
template<class Key, template <class...T> class Value, typename T> // detect arithmatic types
class dict<Key, Value, T, typename enable_if<is_arithmetic<Value<T>>::value>::type> {
public:
dict() {
// we'll come to you later..
}
};
主文件
#include <iostream>
#include "utils.h"
int main() {
dict<string, vector, string> d("/home/path/to/some/file");
cout << d.size();
}
结果:
error: no member named 'emplace_hint' in 'std::vector<std::__cxx11::basic_string<char>, std::allocator<std::__cxx11::basic_string<char> > >'
result.emplace_hint(it, static_cast<T>(tokens[i]));
问题: 1 - 为什么首先达到
地球条件?
2 - 如何调整它以实现所需的实例化风格?if (false)
解决方案
if (false)
并不意味着代码没有被编译;它只是意味着里面的代码不会在运行时执行,但它仍然必须是有效的。
C++ 中有(至少)三种条件结构:
预处理器条件。如果条件不满足,这告诉预处理器不要将代码传递给编译器。因此,只要包含有效的预处理器标记,代码就可能完全是乱码。例如,以下是具有定义行为的格式良好的 C++ 程序:
#include <iostream> int main() { #if 0 YYMJBNvOldLdK8rC0PTXH8DHJ58FQpP0MisPZECDuYHDJ7xL9G #else std::cout << "Hello world!\n"; #endif }
运行时选择语句。这样的代码仍然由编译器解析,并且无论编译器是否能够证明无法访问的代码都必须仍然是有效代码——
}
如果它不解析代码,编译器甚至找不到终止符。这部分是因为编译器无法在运行时评估任意表达式 - 如果您没有明确指定(参见下一个项目符号),那么评估默认为运行时。因此,如果将#if 0
–#else
–#endif
替换为if (false) {
–} else {
– ,上面的代码就会变得不正确}
。但是,无法访问的代码中的运行时错误(即未定义的行为)是可以的。因此,以下是具有已定义行为的格式良好的 C++ 程序:(某些编译器可能会生成警告,但这无关紧要)#include <iostream> #include <climits> int main() { if (false) { volatile int x = 42/0; x = *static_cast<int*>(nullptr); const int y = INT_MAX + 1; } else { std::cout << "Hello world!\n"; } }
(C++17 起)
if constexpr
。这个规则有点复杂。必须在编译时知道条件,并丢弃false
分支。丢弃的分支仍然需要有效,只是它没有被实例化。因此,如果您将上述代码更改为. 以下也是具有已定义行为的格式良好的 C++ 程序:if
if constexpr
#include <iostream> #include <type_traits> template <typename T> void print(T x) { if constexpr (std::is_same_v<T, int>) { std::cout << static_cast<typename T::template stack<T>::overflow>(x); } else { std::cout << x; } } int main() { print("Hello world!\n"); }
typename
和template
仍然是使代码在语法上有效的必要条件,但不存在的类型const char*::stack<const char*>::overflow
不会形成。
在您的情况下,您可以编写一个特征类来确定一个类型是否是类模板的特化std::vector
:(这里我使用标准特征约定)
template <typename C>
struct is_std_vector :std::false_type {};
template <typename T, typename A>
struct is_std_vector<std::vector<T, A>> :std::true_type {};
template <typename C>
inline constexpr bool is_std_vector_v = is_std_vector<C>::value;
然后使用它if constexpr
来调度:(不要忘记替换Container
为您正在检查的容器类型)
if constexpr (is_std_vector_v<Container>) {
// do std::vector specific things
} else {
// do other things
}
推荐阅读
- javascript - 有什么方法可以使数组的每个索引与数组中的其他索引匹配,但不与自身匹配
- javascript - 从浏览器控制台下载图片链接 (javascript)
- docker - 卡夫卡 | 错误:无法打开 HTTP 服务器:socket.error 报告 errno.ENOENT (2)
- python - 具有相反严重性级别的分类值的多列标准化
- postgresql - 如何将 9 个随机行从一个表插入另一个表,同时在 Postgresql 中设置其他列?
- html - BootstrapVue:创建 2 个底部导航栏粘在顶部的导航栏?
- image - 从网页导出 SVG 图像
- geolocation - Mapbox GL JS - 如何检查一个人是否在某个区域内,如果他在,则显示一条消息
- r - 将所有二进制 (0, 1, NA) 变量转换为因子
- python - 帮助医疗保健,python 代码根据质量计算 AR 中有多少日托有什么容量