【问题标题】:Templates and preprocessing in C++C++ 中的模板和预处理
【发布时间】:2014-07-23 16:46:23
【问题描述】:

有人如何编译这段代码:

#include <iostream>
using namespace std;

enum E { A, B};

template< E x>
class C {
public:
    #if( x == A)
        static void foo() {
            cout << "A";
        }
    #elif( x == B)
        static void goo() {
            cout << "B";
        }
    #endif
};


int main() {
    C< A>::foo();
    C< B>::goo();

    return 0;
}

error: ‘goo’ is not a member of ‘C<(E)1u>’

我有两个大类,只有几行不同,所以我想制作枚举模板。问题是这行有using 关键字,所以我不知道该放什么。

有什么正确的方法吗?

PS> 我必须使用 C++03。

编辑:稍微澄清一下。我知道template&lt;&gt; 构造,但我不想使用它,因为这样我得到了很多代码重复。也许我可以以某种方式进行部分“模板实例化”(如果这是template&lt;&gt; 的正确术语)?

假设我有两个班级(我不会有更多):

class A {
public:
//…a lot of code…
//few lines that differs in A and B
}

class B {
//…the same mass of code…
//few lines that differs in A and B
}

所以我决定在enum上制作模板:

enum E { A, B}
template< E>
class C{
//…common code…
}

现在我不知道如何处理这在 A 和 B 中不同的几行。我知道常见的方法是进行模板实例化,但是我会得到与 AB 类完全相同的内容。

从 OOP 的角度来看,我应该对 AB 使用常见的 Base。但问题是AB 已经是一样的了。它们仅与以下行不同:

using CanStoreKeyValue< QString, Request>::set;
using CanStoreKeyValue< QString, Response>::set;

其中ResponseRequest 是类型定义。此外,在我的代码中,AB 是同一个模板抽象类的子类。当然,他们使用不同的模板参数继承它。这以某种方式破坏了模板枚举的使用——编译器只是看不到某些虚拟方法不再是纯的。所以……这就是为什么我要问我在问什么。我认为预处理器可以通过#if-directives 与模板引擎进行交互(最后,它们都是编译时进程)。

【问题讨论】:

  • 预处理发生在其他任何事情之前。预处理器指令(以# 开头)不知道ABx 甚至存在。
  • 我不明白你在做什么。为什么不创建两个类,一个是 foo,一个是 goo?
  • 如果方法与参数不兼容,您是否尝试获得编译时错误,或者您是否尝试进行某种编译时调度?
  • 您的预处理器指令(任何以 # 开头的指令)在编译器看到之前就删除了 goo() 的定义。
  • @ValentinT。为什么不将请求/响应类型作为模板参数而不是 A、B?

标签: c++ templates if-statement enums c++03


【解决方案1】:

您不能使用预处理器来实现您想要做的事情。您需要的是模板专业化:

enum E { A, B};

template< E x>
class C;

template <>
class C<A> {
public:
    static void foo() {
        cout << "A";
    }
};

template <>
class C<B> {
public:
    static void goo() {
        cout << "B";
    }
};

您更新后的问题仍然可以通过模板专业化解决:

enum E { A, B };

template< E x >
struct CanStoreKeyValueHelper;

template<>
struct CanStoreKeyValueHelper<A> {
    typedef CanStoreKeyValue< QString, Request>::set type;
};

template<>
struct CanStoreKeyValueHelper<B> {
    typedef CanStoreKeyValue< QString, Response>::set type;
};

template< E x>
class SuperPuperBigClass {
public:
    typedef typename CanStoreKeyValueHelper<x>::type set;
    ...
};

但不清楚你为什么不把它简化为:

template<class T>
class SuperPuperBigClass {
public:
    typedef typename CanStoreKeyValue<QString, T>::set set;
};

并用请求和响应类型实例化它

【讨论】:

  • 是的,我知道,但是在 C++ 中有一些方法可以在 C&lt;A&gt;goo 中使用 fooC&lt;B&gt; 中不使用“模板实例化”(我不不知道,template&lt;&gt;) 是正确的说法吗?
  • 我不想使用template&lt;&gt; 构造,因为这样我会有很多代码重复。
  • @ValentinT.:将重复代码移动到共享基类中。或者,可以使用std::enable_if,但这可能很复杂。
  • 我有两个类继承自具有不同模板参数的同一个模板化抽象类。它们已经从相同的基础继承而来,并且与一些 using 行和纯虚拟实现不同。并且编译器不知何故看不到它们实现了抽象基础。所以,我只想要预处理器#if 构造的构造模拟,但是模板。而且我必须使用C++03,所以我相信std::enable_if不是我的情况。
【解决方案2】:

如果你不想要专业化,我想用SFINAE 来约束函数是一个选项(Live at coliru):

template <E x>
class C {
public:
    template <E y = x>
    static typename std::enable_if<y == A>::type foo() {
        cout << "A";
    }

    template <E y = x>
    static typename std::enable_if<y == B>::type goo() {
        cout << "B";
    }
};

但如果代码重复是您唯一的问题,请使用专业化并将通用代码放在基类 (Live at Coliru) 中,这样会更简洁:

class C_base {};

template <E x>
class C : public C_base {};

template <>
class C<A> : public C_base {
public:
    static void foo() {
        cout << "A";
    }
};

template <>
class C<B> : public C_base {
public:
    static void goo() {
        cout << "B";
    }
};

【讨论】:

  • 感谢您的回答! std::enable_if 不是 C++11 构造吗?我不知道 SFINAE。
  • @ValentinT。糟糕,是的 - 没有注意到 C++03 标签。将规范实现添加到答案中。
  • 我发现enable_if 的这种变体在工作时更容易看:coliru.stacked-crooked.com/a/d0d77cef8f219d21
  • 谢谢@MooingDuck,我认为你的回答是最接近主题问题的。
  • @ValentinT.:我只是用不同的方式重写了这个答案中的代码。如果我写的是正确的,那么凯西的回答更正确。
猜你喜欢
  • 2016-06-30
  • 2011-04-26
  • 2010-11-11
  • 2012-08-14
  • 1970-01-01
  • 1970-01-01
  • 2015-11-21
  • 2011-07-08
  • 2012-06-25
相关资源
最近更新 更多