c++gccgcc8

Why does gcc8.3 appear to attempt to compile an unused template function?


Consider (the name of the file is hello.cpp) this code; the idea is to engineer a safe casting of numeric types without loss or overflow. (I'm porting some code from MSVC to g++).

#include <cstdint>
#include <iostream>

template<
    typename T/*the desired type*/,
    typename/*the source type*/ Y
> T integral_cast(const Y& y)
{
    static_assert(false, "undefined integral cast");
}

// Specialisation to convert std::uint32_t to double
template<>
inline double integral_cast(const std::uint32_t& y)
{
    double ret = static_cast<double>(y);
    return ret;
}

int main()
{
    std::uint32_t a = 20;
    double f = integral_cast<double>(a); // uses the specialisation
    std::cout << f;
}

When I compile with gcc 8.3 by typing g++ -o hello hello.cpp I get the error error: static assertion failed: undefined integral cast.

This means that g++ is always compiling the unused template code.

Note that MSVC compiles this (which is nice since it allows me to spot any integral cast specialisations that I haven't considered).

Clearly I'm missing something. But what?


Solution

  • GCC isn't really "instantiating" or "compiling" the base function template. If it was, you would have two compiled functions with the same name and parameter list. As @Raymond Chen pointed out in the comments, GCC is permitted, but not required, to raise an error for templates that don't have any valid instantiation.

    For example:

    template<
        typename T/*the desired type*/,
        typename/*the source type*/ Y
    > T integral_cast(const Y& y)
    {
            static_assert(sizeof(Y) == 1);
    };
    

    Won't raise an error in the example you give (because it has a valid instantiation and is not instantiated).

    I suspect GCC just needs to substitute the types into the base template for overload resolution, so it really just needs the declaration, not the definition.

    You can get the behavior you want by using a deleted definition:

    template<
        typename T/*the desired type*/,
        typename/*the source type*/ Y
    > T integral_cast(const Y& y) = delete;