避免模​​板特化中的功能定义重复

时间:2011-08-19 03:24:32

标签: c++ templates partial-specialization

类Widget有一些函数适用于所有参数类型(常用函数)和其他需要专门用于给定类型的函数(非常见函数)。

g ++坚持认为Widget的专业化也应该定义common_fn()而不仅仅是uncommon_fn(),但这首先会破坏使用特化的目的。如何避免重复common_fn()?

#include <cassert>

template<typename Type> struct Widget
{
    Widget() {}
    char common_fn() { return 'a'; }
    int uncommon_fn() { return 1; }
};

template<> struct Widget<char>
{
    Widget() {}
    int uncommon_fn() { return 2; }
};

int main()
{
    Widget<char> WidgetChar;
    assert( WidgetChar.common_fn() == 'a' ); // Error
    assert( WidgetChar.uncommon_fn() == 2 );
}

开始-修改

到Alf:

我无法使用

template<> int Widget<char>::uncommon_fn() { return 2; }

因为一些不常见的函数需要返回一个特征类型(因此通过创建实际的类型原语来简化它)。

或者实际上有一种方法可以让编译器在编写

时识别typename Foo::Bar
struct Foo { typedef FooBar Bar; };
template<> typename Foo::Bar Widget<Foo>::uncommon_fn() { return ....; }

最终修改

开始-EDIT2

到iammilind:

这很有意思,但出于同样的原因,我无法使用Widget的派生(或者将公共部分重构为父类GeneralWidget的可能更清晰的解决方案)。共同部分并不完全常见。他们的声明和他们的定义看起来是一样的,但是因为他们使用的是特征,所以他们最终完全不同。

最终EDIT2

4 个答案:

答案 0 :(得分:7)

#include <assert.h>

template<typename Type> struct Widget
{
    Widget() {}
    char common_fn() { return 'a'; }
    int uncommon_fn() { return 1; }
};

template<>
int Widget<char>::uncommon_fn() { return 2; }

int main()
{
    Widget<char> WidgetChar;
    assert( WidgetChar.common_fn() == 'a' ); // OK
    assert( WidgetChar.uncommon_fn() == 2 );
}

答案 1 :(得分:1)

首先,在C ++中,这不是一个真正受支持的事情。模板专门化要求您重新定义所有方法,不幸的是。

你可以将你的常用方法放在基类中,然后通过继承包含它们,虽然我自己的经验是一个混合包(父类中的一个函数的行为与行为不同)在所有情况下,子类中的函数都是函数;友谊工作很奇怪,如果基类也是模板,只要你想使用它的任何成员[MS编译器除外],你必须完全指定它。操作符永远不会像好吧)。您可以通过复制粘贴常用方法来实现,这正是您要避免的。或者,作为复制粘贴的折衷方案,您可以让编译器为您执行复制粘贴:

template<> struct Widget<char>
{
    #include "Widget_common.hpart"
    int uncommon_fn() { return 2; }
};

然后你创建一个名为“Widget_common.hpart”的文件,其中包含

// This file contains common functions for the Widget<> template, and will be #included
// directly into that template; it should not be included as a standalone header.
char common_fn() { return 'a'; }

我想你也可以使用标准的.h扩展名。这绝对是预处理程序滥用,但它可以满足您的要求,避免模板继承问题(并且它们确实非常痛苦),并且只允许您保留公共代码的单个副本。如果有人想批评这是一个多么可怕的kludge,那么使用改进的解决方案来做这件事;)。

答案 2 :(得分:0)

理想情况下,在class专业化的情况下,我们假设专门化所有必需的方法。如果你不想那样,那么下面的技巧就有效(有一些限制):

template<>
struct Widget<char> : Widget<int>
{
  //...
};

为了简单起见,我认为<int>被认为不是专业的(你可以把 {/ 1>}这样的东西放在更安全的一边)。

Widget<void**>

答案 3 :(得分:0)

我确实看过你关于特质的其他帖子,但看到你似乎已经通过决定不使用专业化解决了你的问题。但是,为了造成同样问题的其他人的利益,我还有另一个人们可能想要考虑的解决方案。

您可以将常用功能提取到自己的类中,然后在类专门化中提供包装函数,而不是尝试提取不常见的功能。任何默认构造函数或析构函数甚至都不需要包装!

#include <cassert>

template<typename Type> struct WidgetCommon
{
    char common_fn() { return 'a'; } // 1,000,000 line function here :D
};

template<typename Type> struct Widget
{
    Widget() {}
    char common_fn() { return common.common_fn(); }
    int uncommon_fn() { return 1; }

private:
    WidgetCommon<Type> common;
};

template<> struct Widget<char>
{
    Widget() {}
    char common_fn() { return common.common_fn(); }
    int uncommon_fn() { return 2; }

private:
    WidgetCommon<char> common;
};

int main()
{
    Widget<char> WidgetChar;
    assert( WidgetChar.common_fn() == 'a' );
    assert( WidgetChar.uncommon_fn() == 2 );

    Widget<int> WidgetInt;
    assert( WidgetInt.common_fn() == 'a' );
    assert( WidgetInt.uncommon_fn() == 1 );
}

在这种情况下,它确实增加了代码量,但常见的实现只存在一次。创建更多特化只需要复制包装器样板而不是行为代码。在现实世界中,可能存在数百行公共代码,这种方法可以避免大量重复。