Que*_*tin 6 c++ templates overloading variadic-templates template-argument-deduction
我正在尝试实现一个函数模板ovl,它ovl<Foo, Bar>(f)会返回ftake的重载(Foo, Bar),并且对我天真的解决方案所发生的事情感到非常惊讶:
template <class... Args, class Ret>
constexpr auto ovl(Ret (*const f)(std::type_identity_t<Args>...)) { return f; }
void foo();
void foo(int);
void foo(int, int);
int main() {
    ovl<int>(foo)(0);
}
prog.cc:26:5: fatal error: no matching function for call to 'ovl'
    ovl<int>(foo)(0);
    ^~~~~~~~
prog.cc:6:16: note: candidate template ignored: couldn't infer template argument 'Ret'
constexpr auto ovl(Ret (*const f)(std::type_identity_t<Args>...)) { return f; }
               ^
GCC 和 Clang 出现相同的错误。更重要的是,它实际上在自己枚举可能的参数时起作用:
template <class Ret>
constexpr auto ovl(Ret (*const f)()) { return f; }
template <class Arg0, class Ret>
constexpr auto ovl(Ret (*const f)(std::type_identity_t<Arg0>)) { return f; }
template <class Arg0, class Arg1, class Ret>
constexpr auto ovl(Ret (*const f)(std::type_identity_t<Arg0>, std::type_identity_t<Arg1>)) { return f; }
// ... ad nauseam.
有趣的是,保留Args...但硬编码返回类型也有效:
template <class... Args>
constexpr auto ovl(void (*const f)(std::type_identity_t<Args>...)) { return f; }
似乎部分显式参数在提供给参数包时被忽略,但为什么呢?以及如何确保在尝试消除函数指针歧义时考虑它们?
注意:我发现了以下解决方法,它Args...在推断之前先烘焙Ret,但我仍然对答案感兴趣,因为这很笨拙。
template <class... Args>
struct ovl_t {
    template <class Ret>
    constexpr auto operator()(Ret (*const f)(Args...)) const { return f; }
};
template <class... Args>
constexpr ovl_t<Args...> ovl;
我相信这是一个编译器错误。
正如 @StoryTeller - Unslander Monica 提到的[temp.arg.explicit]
模板参数推导可以扩展与模板参数包对应的模板参数序列,即使该序列包含显式指定的模板参数也是如此。
这意味着即使我们提供了一个int参数,编译器也会尝试推断出更多的参数Args。
如果参数是重载集(不包含函数模板),则尝试使用该集的每个成员进行试验参数推导。如果仅重载集成员之一的推导成功,则该成员将用作推导的参数值。如果对重载集的多个成员推导成功,则该参数将被视为非推导上下文。
试算扣除额为
template<typename... Args, typename Ret>
void trial(Ret(*)(std::type_identity_t<Args>...));
trial<int>((void(*)())foo);         // fails
trial<int>((void(*)(int))foo);      // succeeds
trial<int>((void(*)(int, int))foo); // fails, trailing Args is non-deduced context
意味着仅void(int)使用成员作为参数值,这将成功推导Ret = void和Args = {int}。
| 归档时间: | 
 | 
| 查看次数: | 97 次 | 
| 最近记录: |