mardi 26 mai 2020

C++11: properly expand template parameter pack in trailing return type

I am trying to translate run-time constant to compile-time constant in my C++11 program using switch statement. I have enum SomeEnum {A, B, C};, and depending on its value I want to call the template function f<A>(), f<B>() or f<C>(). I have lots of template functions in my code that depend on SomeEnum (as well as other template parameters), so I decided to make dedicated dispatch function. Here is the code I am trying to compile:

#include <utility>

enum SomeEnum {
      A, B, C
};

template<template<SomeEnum, typename...> class FUNCTOR, typename ...OTHERS, typename ...ARGS> inline auto dispatch (
    SomeEnum const type,
    ARGS&&... args
) -> decltype( FUNCTOR<A, OTHERS...>::function(std::forward<ARGS>(args)...) ) { //Problem here
    switch(type) {
        case A:
            return FUNCTOR<A, OTHERS...>::function(std::forward<ARGS>(args)...);
        case B:
            return FUNCTOR<B, OTHERS...>::function(std::forward<ARGS>(args)...);
        case C:
            return FUNCTOR<C, OTHERS...>::function(std::forward<ARGS>(args)...);
        default:
            //Do not call any function if enum value is wrong, just return something:
            return decltype( FUNCTOR<A, OTHERS...>::function(std::forward<ARGS>(args)...) )();
    }
}

template<SomeEnum T> struct TemplateFunctor1 {
    static inline int function(int) { return 0; }
};

template<SomeEnum T, class OTHER> struct TemplateFunctor2 {
    static inline int function(int) { return 0; }
};

int main(void) {
    dispatch<TemplateFunctor1>(B, 0); //Problem here!

    dispatch<TemplateFunctor2, int>(B, 0);

    return 0;
}

(I do not need to handle the case when function return type depends on template parameter)

I am getting an error while compiling the line dispatch<TemplateFunctor1>(B, 0);, where is only one template parameter required by TemplateFunctor1. The error messages are following:

error: no matching function for call to 'dispatch(SomeEnum, int)'

note: candidate: template<template<SomeEnum <anonymous>, class ...> class FUNCTOR, class ... OTHERS, class ... ARGS> decltype (FUNCTOR<(SomeEnum)0u, OTHERS ...>::function((forward<ARGS>)(dispatch::args)...)) dispatch(SomeEnum, ARGS&& ...)

note: template argument deduction/substitution failed

error: wrong number of template arguments (2, should be 1) in this part of code: FUNCTOR<A, OTHERS...> of trailing return type.

So I tried to change FUNCTOR<A, OTHERS...> in trailing return type to FUNCTOR<A>, then the line dispatch<TemplateFunctor2, int>(B, 0); cannot be compiled. It says that I provided wrong number of template parameters (1, should be 2), which is quite an expected error.

What I do not understand here is why the same decltype() construction works in default switch branch, but does not work in the trailing return type?

I can fix my code by changing the standard to C++14 and by removing trailing return type completely, but then I will not understand what is going on here.

I also tried different compilers (ICC, GCC, Clang), and all of them giving similar error messages, so it cannot be a compiler error here.

Aucun commentaire:

Enregistrer un commentaire