【问题标题】:Specializing and or Overloading member function templates with variadic parameters使用可变参数专门化和/或重载成员函数模板
【发布时间】:2018-03-19 23:34:46
【问题描述】:

尝试解决类成员的重载决议:静态函数模板重载 - 部分特化。

我目前有一个这样声明/定义的类:

注意:我对@9​​87654321@、Param bParam c等的使用与实际的声明/定义没有直接关系。这些可以是传递给函数的任意类型,例如:它可以是int aenum bchar c。我只是用它来显示声明的模式,但是所有不同的引擎都采用相同的 3 个不同的参数。

SomeEngine.h

#ifndef SOME_ENGINE_H
#define SOME_ENGINE_H

class SomeEngine {
public:
    SomeEngine() = delete;

    static engineA& getEngineA( Param a, Param b, Param c );
    static engineB& getEngineB( Param a, Param b, Param c );
    // ... more static functions to return other engines

    template<class Engine>
    static Engine& getEngine( Param a, Param b, Param c );
};

// Another class

// function template that uses both classes above

#endif // SOME_ENGINE_H

SomeEngine.cpp

#include "SomeEngine.h"

template<>
EngineA& SomeEngine::getEngine( Param a, Param b, Param c ) {
    return getEngineA( a, b, c );
}

template<>
EngineB& SomeEngine::getEngine( Param a, Param b, Param c ) {
    return getEngineB( a, b, c );
}

上面的函数模板设计模式,我能够使用单个通用 getEngine() 调用来专门化类以返回适当的引擎类型,编译并且工作正常。我有一个非类成员函数模板,它采用class Engine 作为其模板参数之一...这是在上面任何类之外的同一个标题中定义的,并且在它将使用的前两个类之后。

template<class Engine, typename T>
T generateVal( Param a, Param b, Param c ) {
    static T retVal = 0;
    static Engine engine = SomeEngine::getEngine<Engine>( a, b , c );
}

除了这里显示的功能不完整之外,上述工作。它依赖于另一个类。另一个类本身具有与上述类似的模式;它有一个已删除的默认构造函数,以及一堆返回不同类型对象的静态方法;但是在第二类中,几乎所有的静态方法本身都是函数模板,有些具有重载版本,而另一些则具有多个模板参数。它也在上面的同一个头文件中声明。它看起来像这样:

class SomeOther {
public:
    SomeOther() = delete;

    template<class IntType = int>
    static otherA<IntType>& getOtherA( IntType a, IntType b );

    template<class RealType = double>
    static otherB<RealType>& getOtherB( RealType a, RealType B );

    template<class IntType = int>
    static otherC<IntType>& getOtherC( IntType a );

    template<class RealType = double>
    static otherD<RealType>& getOtherD( RealType a );

    template<class IntType = int>
    static otherE<IntType>& getOtherE();

    template<class IntType = int>
    static otherE<IntType>& getOtherE( IntType a, IntType b );

    template<class IntType = int>
    static otherE<IntType>& getOtherE( std::initializer_list<double> a );

    template<class IntType = int, class X>
    static otherE<IntType>& getOtherE( std::size_t a, double b, double c, X x );

};

我正在尝试对上面的第二个类做类似的事情,以获得一个通用函数模板,这样我就可以将模板参数 class Other 传递给它,除了 class Other 取决于它自己的模板参数和内部函数调用可能有不同数量的参数。

这让我开始使用可变参数模板来声明这个类的函数。

我曾尝试过这样的事情:

template< typename Type, 
          template<typename, class...> class Other,
          class... OtherParams, 
          class... FuncParams> 
static Other<Type, OtherParams...>& getOther( FuncParams... params );

然后我上面显示的功能不完整,我在添加对第二类的支持时尝试了这个:

template< class Engine, 
          typename Type, 
          template<typename, class...> class Other,
          class... OtherParams,
          class... FuncParams>
Type generate( Param a, Param b, Param c, FuncParams... params ) {
    static Type retVal = 0;
    static Engine engine = SomeEngine::getEngine<Engine>( a, b, c );
    static Other<Type, OtherParams...> other = SomeOther::getOther<Type, Other<Type, OtherParams...>> ( params... );
    retVal = other( engine );
    return retVal;
}

这就是我将如何使用上面的第二类。这是我尝试在相应的 cpp 文件中专门化几个 getOther() 函数的尝试

template<typename Type,
        template<typename, class...> class Other,
        class... OtherParams,
        class... FuncParams>
otherA<Type>& SomeOther::getOther( FP... params ) {
    return getOtherA( params... );
}

template<typename Type,
         template<typename, class...> class Other,
         class... OtherParams,
         class... FuncParams>
otherB<Type>& SomeOther::getOther( FP... params ) {
     return getOtherB( params... );
}

这不会编译它抱怨函数定义与现有声明不匹配。我什至试图在头文件中写一个重载,但我一直收到同样的错误。我不知道这是否是语法错误。我已经尝试了很多东西来在这里列出,我已经在整个地方搜索了类似的东西,但似乎找不到任何相关的东西。

我想知道这样的事情是否可以做到;如果是这样,上面需要更改什么才能至少编译和构建;所以我可以在运行时开始测试它;然后继续添加其他现有类型。

我想尝试保持第一类与第二类相同的设计模式。我的独立函数模板是将被调用的函数,取决于它的模板参数;它应该知道要调用哪个引擎-其他类型。

【问题讨论】:

  • 您似乎将Other 有时用作模板,有时用作类型。那不会很顺利。
  • @aschepler 这就是我在声明/定义方面遇到的麻烦。
  • 我正在尝试编写一个通用函数模板,它将从第二类中选择适当的函数模板;然而,其中一些函数模板可能有多个模板参数,并且它们的参数列表各不相同。使用引擎很容易,因为它们都不是模板,并且它们都具有相同的 3 个参数。
  • 到底Other应该是一个类对象;但例如:otherA&lt;int&gt; a 可能是一种类型,而 otherB&lt;double, class A, class B&gt; b。其中a's 构造函数可能有(int a, int b)b's 构造函数可能有( A a1, A a2, B b1 )。我需要为调用这些方法的通用函数模板解析所有参数类型。
  • @aschepler 我已经将类的名称从Other 更改为SomeOther,这样带有class Other 模板参数参数列表的函数声明就不会让您感到困惑或冲突;在我的实际代码中,它们的名称不同。

标签: c++ templates overloading variadic-templates partial-specialization


【解决方案1】:

在第二种情况下,实际上您试图部分专门化getOther。部分特化应该这样写:

class Tmp
{
public:
template<class A, class B>
void bar(A a, B b) {}

};


template<class A>
void Tmp::bar<A, int>(A a, int b) {}

并且 GCC 会给出错误提示:

error: non-class, non-variable partial specialization 'bar<A, int>' is not allowed
 void Tmp::bar<A, int>(A a, int b) {}

因此,在第二种情况下,您实际上是在实现您之前声明的方法,而编译器无法找到匹配的声明。

为了解决这个问题,由于 C++ 不允许偏特化,你可以使用函数重载。这是一个示例(它在 GCC 上编译但在 VS2017 上编译失败,更像是 MSVC 的问题,但我不确定):

template <class T>
class otherA 
{
    T t;
public:
    otherA(T t) : t(t) {}

    void WhoAmI() { cout << "I'm OtherA" << endl; }

    T getValue() { return t; }
};

template <class T, class X>
class otherE 
{
    T t;
public:
    otherE(T t) : t(t) {}

    void WhoAmI() { cout << "I'm OtherE" << endl; }

    T getValue() { return t; }
};

class SomeOther {
public:
    SomeOther() = delete;

    template<class IntType = int>
    static otherA<IntType>& getOtherA(IntType a, IntType b) 
    {
        static otherA<IntType> A(a);
        return A;
    }

    template<class IntType = int, class X>
    static otherE<IntType, X>& getOtherE(IntType a, double b, double c, X x)
    {
        static otherE<IntType, X> E(a);
        return E;
    }

    template<template<typename, class...> class Other,
        class Type,
        class... OtherParams,
        class... FuncParams>
    static Other<Type, OtherParams...>& getOther(FuncParams... params)
    {
        return getOther<Type, OtherParams...>(params...);
    }

private:
    /// Function Overloading

    template<class T,
        class... FuncParams>
    static otherA<T>& getOther(FuncParams... params)
    {
        return getOtherA<T>(params...);
    }

    template<class T,
        class X,
        class... FuncParams>
    static otherE<T, X>& getOther(FuncParams... params)
    {
        return getOtherE<T, X>(params...);
    }
};

template<
    class Type,
    template<typename, class...> class Other,
    class... OtherParams,
    class... FuncParams>
Type test(FuncParams... params) {
    static Other<Type, OtherParams...>& other = SomeOther::getOther<Other, Type, OtherParams...>(params...);
    other.WhoAmI();
    return other.getValue();
}

class foo{};

int main()
{
    int AValue = test<int, otherA>(1, 2);
    cout << "AValue: " << AValue << endl;
    int EValue = test<int, otherE, foo>(3, 2.1, 2.2, foo());
    cout << "EValue: " << EValue << endl;
    return 0;
}

输出将是

I'm OtherA
AValue: 1
I'm OtherE
EValue: 3

【讨论】:

  • 上面的概念似乎还可以;但这里有一个问题:我将返回的另一个是在 &lt;random&gt; 头文件中找到的不同类型的分布。这些类已经声明。我只是想funnel 返回适当分布所需的类型。你看我有静态函数来通过独立的名称返回每个分布,例如std::uniform_int_distribution&lt;IntType&gt;&amp; Bar::getUniformIntDistriubtion( IntType a, IntType b ); 所以我不想重写类。我想要一个 getDistriubtion&lt;Type, DistType&gt;( params... ); 函数。
  • @FrancisCugler 对不起,但我真的不明白这一点。您不需要重写otherA/otherB/etc..,而只需更改BarBar 是你自己的实现,对吧?
  • Bar 本身是一个非模板类,它只有静态方法来获取不同类型的分布。每个静态方法本身已经是函数模板。现在,我只是尝试编写一个函数模板,以根据指定分布的函数模板参数列表通用地获取特定分布,然后该分布将使用什么类型。
  • 这个较旧的问题可能会对您有所帮助,因为它实际上包含我的课程(至少部分课程)。 stackoverflow.com/questions/49336082/…
  • @FrancisCugler 当otherAstd::uniform_int_distribution 时,我仍然没有看到任何区别。您之前所做的是为每个发行版专门化模板,而现在,您必须为每个发行版编写一系列重载。我是不是什么都没看到?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-05-03
  • 1970-01-01
  • 2016-05-15
  • 2011-04-01
  • 1970-01-01
相关资源
最近更新 更多