10

Sorry for the non-trivial code (also on the wandbox here )

#include <memory>
#include <cstdio>

template< typename T, size_t N, typename ... Args >
 void test_1 ( Args ... args)
 {
    using namespace std;

    if constexpr( 1 > (sizeof... (args))  ) {
         return;
    }
    else 
    {
    auto    arg_list [[maybe_unused]] = { args ... };
    }
 }
    template< typename T, size_t N, typename ... Args >
    void test_2 ( Args ... args)
    {
      auto  arg_list [[maybe_unused]] = { args ... };
    }
  ///////////////////////////////////////////////////////////
 int main()
 {
     test_1<int,3>(1,2,3);
     test_2<int,3>(1,2,3);

// taking the function pointer (aka address of) leads to
// full instantiation of function template 
constexpr auto adr_1 [[maybe_unused]] = std::addressof(test_1<int,3>);
// no can do --> constexpr auto adr_2 = std::addressof(test_2<int,3>);

}

So. From the above, it appears taking the template function pointer (aka address of) leads to the full instantiation of a function template.

Which is kind-of-a unfortunate. If one takes the address of template function instantiation, that will produce a full instantiation. Even if the one is never to be called.

     // pointer to instance made but instance never used
     constexpr auto adr_5 [[maybe_unused]] = std::addressof(test_1<float,9>);
     // pointer to instance made but instance never used
     constexpr auto adr_of_big_foot_gun 
           [[maybe_unused]] = std::addressof(test_1<bool,99>);
     // pointer to instance made but instance never used
     constexpr auto adr_of_crazy [[maybe_unused]] = 
                 std::addressof(test_1<char, 0xFFFF>);

Now. How can this be circumvented? Please note the above code means that compiler does this :

        // this line
        std::addressof( test_2<int,3> )

        // provokes something like this
        // which needs to be compiled
        test_2<int,3>(void) ;

This is why one can take the address of test1 above but not test_2. Perhaps to me, this does not make any sense at all. And by this, I specifically mean compiling the function template instance as if by calling it with the void arguments? Which in turn means, without constexpr-if one would be hard-pressed to write test_1 above. Which in turn (almost) means no c++11 and no c++14.

Please discuss ...

  • There's no way to circumvent this. Something needs to exist to have an address. What exactly would you expect the address of your uninstantiated function template specialization to be? – Michael Kenzel Jul 26 '18 at 10:40
  • So, you want to avoid instantiation, but what pointer value do you expect? Do you just need function type? – ivaigult Jul 26 '18 at 10:40
  • Are you trying to do an optimization like "function is instantiated when the function pointer is called"? If so, this is impossible, since c++ does this tricks at compile time only. If you are OK with `adr_1 == nullptr`, then just `typedef` your function pointer type and assign it to `nullptr`. – ivaigult Jul 26 '18 at 10:47
  • Yes, this is exactly what would be a rather nice thing to have. in the compiler that is. –  Jul 26 '18 at 11:29
  • Notice that `std::addressof( test_1 ) != std::addressof( test_1 )`, so the address of the invalid `test_2` would be useless anyway. – Jarod42 Jul 26 '18 at 11:52

2 Answers2

6

Which is kind-of-a unfortunate. If one takes the address of a template function instantiation, that will produce a full instantiation.

It is not unfortunate. That is how the language works. If you get an address of template function (function instantiated from the function template), this function must exist. In other words, it must be instantiated from the corresponding function template.

How can this be circumvented?

There is no way. It is an expected behaviour which makes a perfect sense.

Edgar Rokjān
  • 17,245
  • 4
  • 40
  • 67
  • thanks for the answer. Perhaps to me, this does not make any sense at all. And by this, I specifically mean compiling the fun template instance as if by calling it with the void arguments. Which in turn means, without `constexpr-if` one would be hard pressed to write test_1 above. Which in turn (almost) means no c++11 and no c++14. –  Jul 26 '18 at 11:14
  • 1
    @Edgar Rokjan "You can't specialize function templates". I think you can? https://de.cppreference.com/w/cpp/language/template_specialization – phön Jul 26 '18 at 11:19
  • @phön I lost an important word: partially. Many thanks for reviewing! – Edgar Rokjān Jul 26 '18 at 11:21
  • @phön, and I removed the word "specialization" form the question as it is irrelevant for the use case in which context Edgar was right. –  Jul 26 '18 at 11:24
  • 3
    Function template is just this: a template. It doesn't exist in real program until it is instantiated. You can't take address of something that doesn't exist. If you wan't to instantiate some particular version of template function, you can pass these type arguments: `test_2` etc. – Michał Łoś Jul 26 '18 at 11:25
  • @MichałŁoś How to get address of fully specialized function template? – Sourav Kannantha B Jan 23 '23 at 18:27
  • @SouravKannanthaB: this should be pretty straightforward: https://godbolt.org/z/b7rWq9Kn9 – Michał Łoś Jan 24 '23 at 20:05
  • @MichałŁoś Can you answer [this](https://stackoverflow.com/q/75256860/12357035)? – Sourav Kannantha B Jan 27 '23 at 10:32
1

In order to take an address, you need a complete instantiation, otherwise there is nothing to point to. Remember that the compiler will not be there at runtime to finish generation of a partially parametrized template. Even at a different callsite that only knows about the function pointer, the compiler cannot instantiate the template, because it may not know about it.

The instances of test2<int, 3, ...> (or test1 for that matter) are different functions, depending on the arguments, there can never be one address for all of them. For example, test2<int,3, int> is different from test2<int, 3, int, int>, it takes different arguments and does different things with them. Their addresses are different, as is the type of the corresponding function pointer.

Potentially, depending on how you use the addresses, you may be able to switch them off at compile time by template parametrisation (if the pointers themselves are part of a template that is or is not instantiated) or constexpr-if constructs.

Again potentially, if there remains no reference whatsoever to the pointers in all of the object files, the linker may remove the unnecessarily instantiated templates as well with the right options, but this is not guaranteed, and you will still pay the compile time cost.

PaulR
  • 3,587
  • 14
  • 24