首页 > 解决方案 > 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)

标签: c++c++11dictionaryconditional-statements

解决方案


if (false)并不意味着代码没有被编译;它只是意味着里面的代码不会在运行时执行,但它仍然必须是有效的。

C++ 中有(至少)三种条件结构:

  1. 预处理器条件。如果条件不满足,这告诉预处理器不要将代码传递给编译器。因此,只要包含有效的预处理器标记,代码就可能完全是乱码。例如,以下是具有定义行为的格式良好的 C++ 程序

    #include <iostream>
    
    int main()
    {
    #if 0
        YYMJBNvOldLdK8rC0PTXH8DHJ58FQpP0MisPZECDuYHDJ7xL9G
    #else
        std::cout << "Hello world!\n";
    #endif
    }
    
  2. 运行时选择语句。这样的代码仍然由编译器解析,并且无论编译器是否能够证明无法访问的代码都必须仍然是有效代码——}如果它不解析代码,编译器甚至找不到终止符。这部分是因为编译器无法在运行时评估任意表达式 - 如果您没有明确指定(参见下一个项目符号),那么评估默认为运行时。因此,如果将#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";
        }
    }
    
  3. (C++17 起) if constexpr。这个规则有点复杂。必须在编译时知道条件,并丢弃false分支。丢弃的分支仍然需要有效,只是它没有被实例化。因此,如果您将上述代码更改为. 以下也是具有已定义行为的格式良好的 C++ 程序:ifif 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");
    }
    

    typenametemplate仍然是使代码在语法上有效的必要条件,但不存在的类型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
}

推荐阅读