RenatoUtsch
RenatoUtsch

Reputation: 1559

Variadict template that calls variadic template received as parameter

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?

Upvotes: 4

Views: 87

Answers (1)

user7860670
user7860670

Reputation: 37523

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;
}

Upvotes: 5

Related Questions