为什么 std::is_same 总是给我一个编译错误

Why does std::is_same always give me a compiling error

在我的项目中,有一些配置文件。我需要将它们提取为整数或字符串。为此,我编写了如下函数:

template<typename T>
void loadTxtConfig(const std::string& filename, std::set<T>& ids) {
    std::ifstream infile(filename);
    if (!infile) {
        return;
    }
    std::string line;
    if (std::is_integral<T>::value) {
        while (std::getline(infile, line)) {
            ids.emplace(static_cast<T>(std::stoll(line))); // noexcept
        }
    } else if (std::is_same<T, std::string>::value) {
        while (std::getline(infile, line)) {
            ids.emplace(line);
        }
    }
}

我试着这样称呼它:

std::set<int> intCfg;
std::set<std::string> strCfg;
loadTxtConfig("./conf/int.txt", intCfg);
loadTxtConfig("./conf/str.txt", strCfg);

但是我编译的时候总是报模板错误:

/usr/include/c++/5/ext/new_allocator.h:120:4: error: cannot convert ‘std::__cxx11::basic_string<char>’ to ‘int’ in initialization
  { ::new((void *)__p) _Up(std::forward<_Args>(__args)...); }

我做错了什么?

问题是,ifstatement-truestatement-false 都需要在编译时有效-time,即使其中之一也不会在 运行-time 进行评估。当 Tintids.emplace(line); 会导致错误。

您可以使用 Constexpr If(C++17 起)。

If the value is true, then statement-false is discarded (if present), otherwise, statement-true is discarded.

if constexpr (std::is_integral<T>::value) {
    while (std::getline(infile, line)) {
        ids.emplace(static_cast<T>(std::stoll(line))); // noexcept
    }
} else if constexpr (std::is_same<T, std::string>::value) {
    while (std::getline(infile, line)) {
        ids.emplace(line);
    }
}

在 C++17 之前,您可以应用重载。例如

template<typename T>
void loadTxtConfig(const std::string& filename, std::set<T>& ids) {
    std::ifstream infile(filename);
    if (!infile) {
        return;
    }
    std::string line;
    while (std::getline(infile, line)) {
        ids.emplace(line);
    }
}

void loadTxtConfig(const std::string& filename, std::set<int>& ids) {
    std::ifstream infile(filename);
    if (!infile) {
        return;
    }
    std::string line;
    while (std::getline(infile, line)) {
        ids.emplace(static_cast<T>(std::stoll(line))); // noexcept
    }
}

在 C++11 中,您必须为不同的类型提供不同版本的函数。

void loadTxtConfig(const std::string& filename, std::set<std::string>& ids) {
    std::ifstream infile(filename);
    if (!infile) {
        return;
    }
    std::string line;
    while (std::getline(infile, line)) {
        ids.emplace(line);
    }
}

//you could add std::enable_if to only limit the function to integral types, 
//but it will only compile if `static_cast<T>(long long)` is valid
template<typename T>
void loadTxtConfig(const std::string& filename, std::set<T>& ids) {
    std::ifstream infile(filename);
    if (!infile) {
        return;
    }
    std::string line;
    while (std::getline(infile, line)) {
        ids.emplace(static_cast<T>(std::stoll(line))); // noexcept
    }
}

在 C++11 中,我建议您分解参数特定部分(转换)并使用 SFINAE(替换失败不是错误)来使用正确的部分:

#include <type_traits>

template<typename T, typename std::enable_if<std::is_integral<T>::value, int>::type = 0>
T convert(const std::string& line) {
    return static_cast<T>(std::stoll(line)); // noexcept
}

template<typename T, typename std::enable_if<std::is_same<T, std::string>::value, int>::type = 0>
const T& convert(const std::string& line) {
    return line;
}

template<typename T>
void loadTxtConfig(const std::string& filename, std::set<T>& ids) {
    std::ifstream infile(filename);
    if (!infile) return;
    std::string line;
    while (std::getline(infile, line)) {
        ids.emplace(convert<T>(line));
    }
}