【问题标题】:C++ template instantiation restrictionsC++ 模板实例化限制
【发布时间】:2010-06-30 17:37:59
【问题描述】:

我在 C 类中有一个方法 foo,它调用 foo_1 或 foo_2。 这个方法 foo() 必须在 C 中定义,因为 foo() 在 BaseClass 中是纯虚拟的,我实际上 必须制作 C 类型的对象。代码如下:

template <class T>
class C:public BaseClass{

  void foo() {
    if (something()) foo_1;
    else foo_2;

  }
  void foo_1() {
  ....
  }

  void foo_2() {
    ....
     T t;
     t.bar(); // requires class T to provide a method bar() 
     ....
   }
};

现在对于大多数类型 T foo_1 就足够了,但对于某些类型 foo_2 将被调用 (取决于某事())。然而,编译器坚持实例化 foo_1 和 foo_2 因为两者都可能被调用。

这给 T 带来了它必须提供的负担 一个酒吧方法。

我如何告诉编译器以下内容:

  • 如果 T 没有 bar(),仍然允许它作为实例化类型吗?

【问题讨论】:

  • 我的意思是 foo() 在 BaseClass 中是纯虚拟的。
  • @and 他的班级名称是'C'。我不认为他指的是 C 语言。
  • 什么是something()?它是一些编译时常量吗?
  • @Georg:在运行时没有评估任何东西()。
  • @Georg +1。如果是,那将是一个非常容易应用的解决方案。

标签: c++ templates instantiation


【解决方案1】:

您可以使用boost.enable_if。像这样:

#include <boost/utility/enable_if.hpp>
#include <iostream>

struct T1 {
    static const bool has_bar = true;
    void bar() { std::cout << "bar" << std::endl; }
};

struct T2 {
    static const bool has_bar = false;
};

struct BaseClass {};

template <class T>
class C: public BaseClass {
public:
    void foo() {
        do_foo<T>();
    }

    void foo_1() {
        // ....
    }

    template <class U>
    void foo_2(typename boost::enable_if_c<U::has_bar>::type* = 0) {
        // ....
        T t;
        t.bar(); // requires class T to provide a method bar() 
        // ....
    }

private:

    bool something() const { return false; }


    template <class U>
    void do_foo(typename boost::enable_if_c<U::has_bar>::type* = 0) {
        if (something()) foo_1();
        else foo_2<U>();
    }

    template <class U>
    void do_foo(typename boost::disable_if_c<U::has_bar>::type* = 0) {
        if (something()) foo_1();
        // I dunno what you want to happen if there is no T::bar()
    }
};

int main() {
    C<T1> c;
    c.foo();
}

【讨论】:

  • 我们可以在不使用 boost 的情况下做到这一点吗?
  • 请看stackoverflow.com/questions/2937425/… - 你需要模板化“do_foo”
  • @user231536:你可以手动滚动 boost::enable_if 所做的事情,这不是标题的复杂性。但我只会使用那个特定的标题。这个例子(现在编译!)将适用于T1T2,并且只会在类型声称拥有它时尝试调用bar。这似乎是您想要的。
【解决方案2】:

您可以为 foo_1 和 foo_2 创建一个接口,例如:


class IFoo
{
public:
    virtual void foo_1()=0;
    virtual void foo_2()=0;
};

template <typename T> 
class C : public BaseClass, public IFoo
{ 

  void foo()
  { 
    if (something())
        foo_1(); 
    else
        foo_2(); 
   } 
};

template <typename T>
class DerivedWithBar : C<T>
{
public:
    void foo_1() { ... } 

    void foo_2()
    { 
         ... 
         T t; 
         t.bar(); // requires class T to provide a method bar()  
         ...
     }
}; 

template <typename T>
class DerivedNoBar : C<T>
{
public:
    void foo_1() { ... } 
    void foo_2() { ... } 
};

【讨论】:

    【解决方案3】:

    我认为最简单的方法是简单地编写一个'C'可以调用的单独函数模板:

    template <class T>
    void call_bar(T& /*t*/)
    {
    }
    
    template <>
    void call_bar<Something>(Something& t)
    {
        t.bar();
    }
    

    原来的'C'类可以做相应的修改:

    void foo_2() {
      ....
       T t;
       call_bar(t); // does not require T to provide bar() 
       ....
    }
    

    这有一个缺点,您必须明确定义哪些类型的 T 提供 bar 方法,但这几乎是不可避免的,除非您可以在编译时确定所有在其公共接口中提供 bar 方法的类型或者修改所有这些支持 bar 的类型,以便它们共享一些可以在编译时确定的共同点。

    【讨论】:

    • "这有一个缺点,你必须明确定义哪些类型的 T 提供了 bar 方法,但这几乎是不可避免的,除非你可以在编译时确定所有提供了 bar 方法的类型bar 方法在他们的公共接口中”你可以强制他们所有人在他们的公共接口中定义这样的函数。在这些类型的命名空间中定义的 ADL respects and finds 函数。请参阅解释“接口原则”的链接。如果我要使用免费功能,我会以 ADL 为基础。
    • @Johannes 从 OP 所说的来看,something() 不是一个可以在运行时评估的表达式。如果没有更多上下文信息,我们必须假设将为所有类型生成调用 foo_1 和 foo_2(期望 bar)的代码,T。在这种情况下,我们如何利用 ADL?有一个函数 call_bar,它不会在与调用 bar 的函数不同的命名空间中调用 bar,将带有 bar 的类型放在不同的命名空间中并依赖 ADL 调用正确的版本?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多