3

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?

P45 Imminent
  • 8,319
  • 4
  • 35
  • 78
  • 2
    Does this answer your question? [Conditional compilation of templates](https://stackoverflow.com/questions/29468724/conditional-compilation-of-templates): "The standard permits, but does not require, compilers to diagnose templates for which no valid instantiation can be generated." – Raymond Chen May 07 '20 at 14:25
  • @RaymondChen: The answer answers it better. – P45 Imminent May 07 '20 at 14:44

1 Answers1

4

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;
Mikel Rychliski
  • 3,455
  • 5
  • 22
  • 29
  • The deleted definition works really well - and the output diagnostic gives you the types, in both MSVC and g++. Thank you very much. – P45 Imminent May 07 '20 at 15:14