【问题标题】:Is there a generic way to forward constructor arguments?有没有一种通用的方法来转发构造函数参数?
【发布时间】:2012-12-16 20:00:36
【问题描述】:

我在下面有一个有效的Cloneable/CloneableImpl 类对。只要我有从子级到父级的默认构造函数,它就可以完成它的工作。

假设 Animal 的构造函数被修改为Animal( std::string const& name ),这需要从子类构造函数中传递名称。

如何在保持Cloneable/CloneableImpl 通用的同时将此要求合并到结构中?

换句话说,我需要能够将所有构造函数参数从 Lion、Tiger 转发到 Animal。有没有办法在 C++11 中以通用方式做到这一点?

如果不可能,如何重组这些模板以保持通用性,同时满足构造函数的要求?

代码

template<typename P>
struct Cloneable
{
    virtual P* clone() const = 0;
};

template<typename T,typename P>
struct CloneableImpl :
    public P
{
    virtual P* clone() const 
    {
        return new T( dynamic_cast<T const&>(*this));
    }
};

// ----------------------------------------------------------------------------

struct Animal :
    public Cloneable<Animal>
{ 
};

struct Lion  : 
    public CloneableImpl<Lion,Animal> 
{ 
};

struct Tiger : 
    public CloneableImpl<Tiger,Animal> 
{ 
};

int
main( int argv, char* argc[] )
{
    Animal* x = new Lion;
    Animal* y = x->clone();

    // we want to do this without hard-coding in template classes
    // Animal* z = new Lion( "Samba" );  
}

【问题讨论】:

  • 听起来像继承的构造函数。不过,还没有多少编译器实现它们。用完美的转发解决方案来模拟它们很难做到绝对正确和绝对通用。
  • 普通的 C++11 完美转发有什么问题,手头的情况
  • 您可以快速编写 90% 的用例解决方案,但要使其完美,需要考虑各种极端情况(例如转发 initializer_lists,或显式构造函数,或者其他;继承 ctors把所有这些都做好)。您还需要小心复制构造函数 (flamingdangerzone.com/cxx11/2012/06/05/is_related.html)...就我个人而言,我一直在使用 90% 的解决方案,并且无休止地推迟思考它,因为它看起来很痛苦:(

标签: c++ templates c++11 constructor clone


【解决方案1】:

在@Cheersandhth.-Alf 和@R 之后。 Martinho Fernandes 在对 OP 的评论中提出的关于完美转发的建议。我研究了一下,想出了这个,这似乎可行。

谢谢大家!

代码

#include <string>
#include <iostream>

template<typename P>
struct Cloneable
{
    virtual P* clone() const = 0;
};

template<typename T,typename P>
struct CloneableImpl :
    public P
{
    template<typename... Args>
    CloneableImpl( Args&&... args ) 
       : P(std::forward<Args>(args)...)
  {  }

    virtual P* clone() const 
    {
        return new T( dynamic_cast<T const&>(*this));
    }
};

// ----------------------------------------------------------------------------

struct Animal :
    public Cloneable<Animal>
{ 
    Animal( std::string const& name ) : m_name( name ) { }

    std::string  m_name;
};

struct Lion  : 
    public CloneableImpl<Lion,Animal> 
{ 
    template<typename... Args>
    Lion( Args&&... args ) 
       : CloneableImpl<Lion,Animal>(std::forward<Args>(args)...)
  {  }
};

struct Tiger : 
    public CloneableImpl<Tiger,Animal> 
{ 
};

int
main( int argv, char* argc[] )
{
    Animal* x = new Lion( "Samba" );
    Animal* y = x->clone();

    std::cerr << y->m_name << std::endl;
} 

【讨论】:

  • @Cheersandhth.-Alf 一个虽小但很棒的提示有很长的路要走,尤其是在不断发展的非平凡语言 C++ 中 - 感谢您的帮助!
  • 为什么不virtual T* clone() const ? C++ 允许返回类型协方差。
  • 这个解决方案的问题是is_constructible&lt;Lion, int&gt; 是真的,但不应该是因为is_constructible&lt;Animal, int&gt; 不是。你可以通过对转发构造函数的约束来解决这个问题,但它变得更加丑陋,并且仍然会出现极端情况错误,这就是为什么它不是 100% 的解决方案。
  • @JonathanWakely:使用当前编译器(截至 2013 年 1 月)is_constructible 对于标准库类型(如std::tuple)不是很可靠。让它适用于用户定义的类型似乎有点矫枉过正。最好等一下……
  • @Cheersandhth.-Alf,你能解释一下你的意思吗?它适用于我的编译器。举个更简单的例子,is_default_constructible&lt;Lion&gt; 为真但is_default_constructible&lt;Animal&gt; 为假,我不知道is_default_constructible 有什么问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-06-30
  • 1970-01-01
  • 1970-01-01
  • 2020-05-02
  • 2016-06-09
相关资源
最近更新 更多