【发布时间】:2019-08-02 21:17:38
【问题描述】:
我正在学习 C++ 中的模板,而我的 C++ 术语集有些有限,所以我无法在 Google 上搜索这个问题。
我正在尝试实现一个基于 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;
这是我正在使用的代码:
utils.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..
}
};
main.cpp
#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 - 为什么首先达到if (false) 条件?
2 - 如何对其进行调整以实现所需的实例化风格?
【问题讨论】:
-
条件未达到。事实上,没有什么是“达到”的;执行甚至没有开始,因为代码没有编译。
-
程序必须格式正确才能编译 - 包括在运行时不会执行的部分。您不能编写
if (false) { random garbage }并期望代码能够编译。实现(我认为)您想要实现的目标的一种方法是通过一个特征类,该类具有统一的接口和您想要支持的每个容器的专门化。 -
“无法访问”是运行时的事情。在代码可以运行之前,它必须被编译。为了被编译,它必须(除其他外)在语法上有效并通过类型检查。
if (false) { int x = "apples" * 1.5; }是一个类型错误,所以它没有通过编译。if (false) { fjakslfkdjfaklsdfjkalskdjf(); }使用了一个未声明的标识符,所以它没有通过编译。 -
我不明白那个评论。错误的代码不会因为你移动它而突然编译。
-
不管你有没有提到,这就是
if (false) {}的意思。意思是“程序运行时不要执行这个块”。这并不意味着“甚至不看这个块中的代码”。
标签: c++ c++11 dictionary conditional-statements