c++ - 派生可变参数类模板的调用函数模板重载

标签 c++ templates c++11

我有一个可变类模板 deriv它派生自可变类模板 base .

我有一个函数模板可以接受任何类型 T ,以及 base<Ts...> 的重载类型;

我怎样才能得到 base<Ts...>传递 const deriv<Ts...>& 时使用的重载?

下面的工作示例:

#include <iostream>
#include <tuple>

template<typename... Ts>
struct base
{
    std::tuple<Ts...> tuple;
};

template<typename... Ts>
struct deriv : base<Ts...>
{
};

//--------------------------------

template<typename T>
void func(const T&)
{
    std::cout << "T" << std::endl;
}

template<typename... Ts>
void func(const base<Ts...>&)
{
    std::cout << "base<Ts...>" << std::endl;
}

//----------------------------------------

int main()
{
    int a;
    base <int, double> b;
    deriv<int, double> c;

    func(a);
    func(b);
    func(c); // <--- I want func<base<Ts...>> not func<T> to be called here

    exit(0);
}

示例输出:

T
base<Ts...>
T

我想要的输出是:

T
base<Ts...>
base<Ts...>

最佳答案

除非您准备好重新设计您的代码,您不能,并且有充分的理由。

func() 的非可变重载是一个比可变参数版本更好的匹配:事实上,当试图解析你的函数调用时,类型参数 T对于非可变重载将被推断为 derived<int, double> .

另一方面,参数包Ts在你的可变参数重载中将被推断为 int, double .类型推导后,这实际上会让编译器有以下两种选择来解析您的调用:

void func(const deriv<int, double>&); // Non-variadic after type deduction
void func(const base<int, double>&);    // Variadic after type deduction

尝试匹配参数类型为 derived<int, double> 的调用时应该选择哪一个?

deriv<int, double> c;
func(c);

显然,第一个非可变重载是更好的匹配

那么如何调用第二个 重载而不是第一个?你有几个选择。首先,您可以通过显式指定模板参数来限定您的调用:

func<int, double>(c);

如果您不喜欢那样,也许您可​​以重新考虑 func() 的非可变重载的定义: 你真的想让它接受任何可能的类型 T ?或者是否有一些您知道不会调用此重载的类型?如果是这样,您可以使用 SFINAE 技术和 std::enable_if排除不需要的匹配项。

作为进一步的可能性,您可以稍微放宽模板函数的签名,并允许将其参数推导为某个模板类的实例:

template<template<typename...> class T, typename... Ts>
void func(const T<Ts...>&)
{
    std::cout << "base<Ts...>" << std::endl;
}

仅此更改就应该按照您想要的方式修复程序的行为。

更新:

如果您希望您的专用函数模板base<> 的任何实例派生的类被调用类模板,您可以使用 std::is_base_of<>类型特征和 std::enable_if通过以下方式:

template<template<typename...> class T, typename... Ts>
void func(
    const T<Ts...>&, 
    typename std::enable_if<
        std::is_base_of<base<Ts...>, T<Ts...>>::value
        >::type* = nullptr
    )
{
    std::cout << "base<Ts...>" << std::endl;
}

附录:

在模板函数重载对您的设计没有帮助的情况下,请注意您始终可以求助于部分模板特化。不幸的是,函数模板不能特化,但您仍然可以利用 class 模板部分特化并添加辅助函数来隐藏该模板的实例化。这就是您重写代码的方式:

namespace detail
{
    template<typename T>
    struct X
    {
        static void func(const T&)
        {
            std::cout << "T" << std::endl;
        }
    };

    template<template<typename...> class T, typename... Ts>
    struct X<T<Ts...>>
    {
        static void func(const T<Ts...>&)
        {
            std::cout << "base<Ts...>" << std::endl;
        }
    };
}

template<typename T>
void func(const T& t)
{
    details::X<T>::func(t);
}

关于c++ - 派生可变参数类模板的调用函数模板重载,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14678330/

相关文章:

c++ - 如何修复错误 "Variable-sized object may not be initialized "?

c++ - 如何延长局部变量的生命周期或使用引用的正确方法是什么

c++ - 有人可以解释这个 c++ 代码的区别吗?

javascript - TemplateUrl angularJS 不工作

c++ - 函数模板内的 decltype 和范围解析运算符

c++ - 禁止自动类型推断

c++ - 在不使用类的情况下隐藏可查询的程序状态?

c++ - 为用户定义的枚举覆盖 std::to_string 是否是为用户定义的枚举提供 to_string 的正确方法?

c++ - 使用 QRegExp 解析 header

c++ - 如何修复 bad_typeid 异常