为派生的可变参数类模板调用函数模板重载

Ste*_*mer 4 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);
}
Run Code Online (Sandbox Code Playgroud)

示例输出:

T
base<Ts...>
T
Run Code Online (Sandbox Code Playgroud)

我希望输出是什么:

T
base<Ts...>
base<Ts...>
Run Code Online (Sandbox Code Playgroud)

And*_*owl 6

除非你准备好重新设计你的代码,否则不能,而且有充分的理由.

你的非变量重载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
Run Code Online (Sandbox Code Playgroud)

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

deriv<int, double> c;
func(c);
Run Code Online (Sandbox Code Playgroud)

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

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

func<int, double>(c);
Run Code Online (Sandbox Code Playgroud)

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

作为另一种可能性,您可以放松模板函数的签名,并允许将其参数推断为某个模板类的实例化:

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

仅此更改应以您希望的方式修复程序的行为.

更新:

如果希望仅对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;
}
Run Code Online (Sandbox Code Playgroud)

附录:

在模板函数重载对您的设计没有帮助的情况下,请注意您始终可以使用部分模板特化.遗憾的是,函数模板不能专门化,但您仍然可以利用模板部分特化并添加辅助函数来隐藏该模板的实例化.这是你重写代码的方法:

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);
}
Run Code Online (Sandbox Code Playgroud)