【问题标题】:Why does std::is_same always give me a compiling error为什么 std::is_same 总是给我一个编译错误
【发布时间】:2021-10-15 15:42:52
【问题描述】:

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

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)...); }

我做错了什么?

【问题讨论】:

  • 在实例化一个函数时,每一行都必须有效(可以编译),即使它永远不会到达。您的代码不符合该假设。您可以在答案中使用if constexpr,或者(C++ 17 之前)您需要重载函数并为不同类型提供不同的版本。
  • 当你只需要 2 种类型的函数(或者通常是有限的一组类型)时,重载比模板更简单

标签: c++ c++11 templates typetraits


【解决方案1】:

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

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

如果值为true,则丢弃statement-false(如果存在),否则丢弃statement-true

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
    }
}

【讨论】:

  • stackoverflow.com/questions/68750800/…。所以如果我必须使用 C++11,在这种情况下我必须使用typeid,对吧?
  • @Yves 不,您不必这样做。 pre C++17 的方式是专门化模板
  • @Yves typeid 也在运行时工作。我认为您需要对 loadTxtConfig 执行专业化或重载。
  • @463035818_is_not_a_number 好吧,看来我别无选择。
  • 你可以创建子函数以避免重复公共代码,尽管对于 pre-c++17。 T convert_to(tag&lt;T&gt;, const std::string&amp;) 似乎足够了。
【解决方案2】:

在 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
    }
}

【讨论】:

  • 你可以创建子函数来避免重复公共代码。
【解决方案3】:

在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));
    }
}

【讨论】:

  • 我只是想做的不仅仅是抱怨;)
  • @463035818_is_not_a_number 我总是感谢您的帮助!干杯!
猜你喜欢
  • 1970-01-01
  • 2016-07-05
  • 2023-04-06
  • 1970-01-01
  • 2021-04-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多