How can I get the signature of a function template without causing instantiation?

Viewed 126

I wondered if there's a specific rules that specified if this code should fail or not:

struct Foo {
    template<typename T>
    int bar() {
        static_assert(!std::is_same<T, float>::value, "Don't send floats please.");
        return 0;
    }

    int baz() { return 0; }
};

int main() {
    using function_type = decltype(&Foo::bar<float>);

    function_type func = &Foo::baz;
    (void) func;
}

This example fails on both GCC and Clang. Why does it have to fail? It seems that taking the type of the function shouldn't cause an instantiation, even less trigger the static assert. Why does it fail? And is there another way to get the signature of a function template without causing it's body to instantiate?

My use case is to reflect on the signature of a function template. The users of my code call my code with some parameters. I then match those parameters to the template argument of the function to take it's signature, and then use the signature to extract the type of the parameters that has not been sent.

When a function takes by forwarding reference, there's no problem. However, if that function receive parameter by value, I should send std::decay_t<Arg> as template argument to not cause compilation errors in the body of the function. But I cannot know if the function I take the signature from receive by value of reference without further reflection capabilities. If I could get the signature of a function template without causing it's instantiation, that would be perfect.

1 Answers

In your case it force calls explicit instantiation, which happens only if template definition are placed in the same compilation unit. If you can split template declarations and definitions in different *.h and *.hpp files (for example) with using the c++11 feature with extern template, then you can use declaration without instantiation (and static_assert calls). Please see the references: https://en.cppreference.com/w/cpp/language/function_template

An explicit instantiation declaration (an extern template) prevents implicit instantiations: the code that would otherwise cause an implicit instantiation has to use the explicit instantiation definition provided somewhere else in the program.

If you want use declaration of special float specialization, then you can use the following code:

#include <type_traits>

struct Foo {
    template<typename T>
    typename std::enable_if<!std::is_same<T, float>::value, int>::type
    bar() {
        static_assert(!std::is_same<T, float>::value, "Don't send floats please.");
        return 0;
    }

    //Provide declaration without any definition
    template<typename T>
    typename std::enable_if<std::is_same<T, float>::value, int>::type
    bar();

    int baz() { return 0; }
};


int main() {
    using function_type = decltype(&Foo::bar<float>);

    function_type func = &Foo::baz;
    (void) func;
}

P.S. Can you explain or add example of your use case (client code)? May be there is an another solution...

Related