4

I have a variadic template function that I want to use to conditionally add arguments to another variadic template function. This is a minimal example, but I can't get it to compile:

// Copyright 2019 Google LLC.
// SPDX-License-Identifier: Apache-2.0

#include <utility>

template<typename... Args>
void f(Args&&... args) {
  // does something with args
}

template<typename T, typename... Args>
void g(T&& t, int i, Args&&... args) {
  if (i != 0) t(i, std::forward<Args>(args)...);
  else t(std::forward<Args>(args)...);
}

int main() {
  g(f, 0, 0);
  return 0;
}

The output from clang for the code above is:

main.cpp:15:7: error: no matching function for call
      to 'g'
      g(f, 0, 0);
      ^
main.cpp:9:10: note: candidate template ignored:
      couldn't infer template argument 'T'
    void g(T&& t, int i, Args&&... args) {
         ^
1 error generated.

With macros, it would work like this (compiles if I replace the g function above with this macro):

// Copyright 2019 Google LLC.
// SPDX-License-Identifier: Apache-2.0

#define g(t, i, args...) \
  if((i) != 0) (t)((i), args); \
  else (t)(args);

Is there a way I can make this work without macros?

1 Answer 1

5

Directly passing f won't work because function pointer must point at specific function template instance that must be instantiated at a point of g invocation. Using template template template parameter it is possible to create function signature builder that can be passed as a parameter without prior instantiation:

#include <utility>

template<typename... Args>
void f(Args... args) {
  // does something with args
}

template<typename... Args> struct
get_f
{
    static constexpr auto & get() { return f<Args...>; }
};

template<template<typename...> typename T, typename... Args>
void g(int i, Args&&... args) {
  if (i != 0) T<int, Args...>::get()(i, std::forward<Args>(args)...);
  else T<Args...>::get()(std::forward<Args>(args)...);
}

int main() {
  g<get_f>(0, 0);
  return 0;
}
Sign up to request clarification or add additional context in comments.

5 Comments

Thanks, that makes a lot of sense. One question though, why can't I specify f<Args...> like you specify T<Args...>? Why doesn't it work as a function pointer to be referenced?
@RenatoUtsch My answer starts from explanation why passing f isn't working.
Yes. This probably comes from my lack of formal knowledge, but why f<Args...> is not a "specific function template instance that is instantiated at the point of g invocation, while get_f<Args...> is a "specific class template instance instantiated at the point of g invocation"?
@RenatoUtsch f<Args...> is specific function template that can be passed around as a reference to function or as a pointer to function. However you are not passing f<Args...> into g, you are passing just f which is only the name of function template which can not be passed anywhere. get_f is only the name of class template but it can be passed at template parameter.
So function templates can't be passed as template arguments but class templates can? Do you know if there is any reason for this? It seems a bit arbitrary.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.