为什么is_integral认为std :: string是整数?

时间:2018-08-13 13:43:46

标签: c++ c++17

这是C ++ 17的简短程序:

#include <iostream>
#include <string>

using namespace std::string_literals;

int main() {
    std::string n = "asdf"s;

    if constexpr (std::is_integral<decltype(n)>::value) {
        std::cout << static_cast<int>(n) << std::endl;
    } else {
        std::cout << n << std::endl;
    }
    return 0;
}

但是它不能编译,因为显然is_integral认为std::string是不可或缺的:

g++ -o main.o -c -std=c++17 -O2 -pipe -fPIC -fno-plt -fstack-protector-strong -Wall -Wshadow -pedantic -Wno-parentheses -Wfatal-errors main.cpp
main.cpp: In function 'int main()':
main.cpp:10:40: error: invalid static_cast from type 'std::__cxx11::string' {aka 'std::__cxx11::basic_string<char>'} to type 'int'
         std::cout << static_cast<int>(n) << std::endl;
                                        ^
compilation terminated due to -Wfatal-errors.

在编译时如何区分可以转换为int的内容和不能转换的内容?

这个问题与“为什么constexpr不会使这个核心常量表达式错误消失?”不一样,因为它与模板无关(即使在这种情况下,正确的用法可能在模板)。即使主题相关,问题本身也有所不同。

此问题与“如果具有非模板类型,则为Constexpr”是不同的,因为它专门涉及std::is_integral的行为。

1 个答案:

答案 0 :(得分:4)

C ++不允许您使用格式错误的代码。让我们举一个简单的例子:

int i = 0;
if constexpr (sizeof(int) > 98) { // never happen
    i = "a string";
}

在这里,编译器很容易看到constexpr的内容将永远无效。就像static_assert(false)一样,总是一个硬错误。这是因为,即使从未在constexpr中调用该代码,也使用了ODR。

那么,为什么模板是您要求的特殊情况?为什么禁用模板中的无效代码有效?事实是,没有特殊情况。将我的代码放在上面并放在模板中:

[](auto) { // this is a template
    int i = 0;
    if constexpr (sizeof(int) > 98) { // never happen
        i = "a string"; // same error
    }
}

这是因为编译器仍然可以证明此代码对于任何模板实例化都是无效的。

但是,如果在constexpr中使用从属指令,则编译器很难“证明”,将不会有任何模板实例化使其生效。看一下这个修改后的示例:

[](auto a) { // this is a template
    if constexpr (sizeof(a) > 98) { // maybe happen
        a = "a string"; // no error?
    }
}

在这里,编译器无法证明a的类型永远不会大于98,也无法证明您无法为其分配字符串文字。可以在代码中的任何地方添加一个可以满足该表达式的新类型。当使用模板类的成员函数时,这是类似的行为:如果该函数在给定模板参数的情况下不会导致有效的实例化,那么只要您不使用该函数,就仍然可以。