简体   繁体   中英

Why does is_integral think that std::string is integral?

Here's a short program for 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;
}

But it does not compile, since apparently, is_integral thinks that the std::string is integral:

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.

How can I differentiate at compile-time between something that can be cast to int , and something that can not?

This question is not the same as "Why doesn't an if constexpr make this core constant expression error dissappear?", since it's not about templates (even though the correct usage, for this case, may be within the context of a template). The question itself is also different, even if the topic is related.

This question is not the same as "Constexpr if with non-template types", because it's specifically about the behavior of std::is_integral .

C++ don't allow you to have ill formed code. Let's take a simple example:

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

Here it's very easy for the compiler to see that the content of the is constexpr will never be valid. Much like static_assert(false) will always be a hard error. This is because even though the code is never called in the constexpr if, the content is ODR used.

Then why templates are a special case you ask? Why disabling invalid code in templates work? The truth is there's no special case. Take my code above and put it in a template:

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

This is because the compiler can still prove that this code will be invalid for any template instantiation.

However, if you use a dependent instruction in the constexpr if, it becomes very hard for the compiler to "prove" there won't be any template instantiation that makes it valid. Take a look at this modified example:

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

Here the compiler cannot prove the type of a won't ever be larger than 98, and cannot prove that you cannot ever assign a string literal to it. It's possible, to add a new type, anywhere in the code, that may satisfy this expression. This is a similar behaviour when using a member function of a template class: if the function won't result in a valid instantiation given template arguments, it's still okay as long as you don't use the said function.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM