【问题标题】:How to allow default construction when using universal reference in constructor?在构造函数中使用通用引用时如何允许默认构造?
【发布时间】:2014-05-05 17:50:41
【问题描述】:

(注意:我找不到合适的标题)

我正在使用 Visual Studio 2013。 我的项目中有以下代码:

template< class ConditionType >
class Not
{
    ConditionType m_condition;
public:
    using this_type = Not<ConditionType>;

    template< class ...Args
        , typename = std::enable_if< 
            !std::is_same< this_type, typename std::decay<Args>::type...>::value 
        >::type
    >
    Not( Args&&... args )
        : m_condition( std::forward<Args>(args)... ) {}


    Not( const Not& ) = default;
    Not& operator=( const Not& ) = default;

    template< class ...Args >
    bool operator()( Args&&... args ) const
    {
        return ! m_condition( std::forward<Args>(args)... );
    }

    friend inline bool operator==( const Not& left, const Not& right )
    {
        return left.m_condition == right.m_condition;
    }

    friend inline bool operator<( const Not& left, const Not& right )
    {
        return left.m_condition < right.m_condition;
    }

};

明显的目的是我有一个 ConditionType,它是一个可比较的函数对象,我想得到一个与之相对应的类型。 例如:

class KeyIsPressed
{
    KeyId m_key;
public:
    KeyIsPressed( KeyId key ) : m_key( std::move(key) ) {}

    bool operator()( const InputStateBuffer& states ) const
    { 
        return states.current().keyboard().is_key_pressed( m_key ); 
    } 
    friend inline bool operator==( const KeyIsPressed& left, const KeyIsPressed& right ) 
    { 
        return left.m_key == right.m_key; 
    } 
    friend inline bool operator<( const KeyIsPressed& left, const KeyIsPressed& right ) 
    { 
        return left.m_key < right.m_key;
    } 
};

void somewhere( const InputStateBuffer& input_state_buffer )
{
    Not<KeyIsPressed> condition { KEY_SHIFT };

    if( condition( input_state_buffer ) ) 
        do_something();
};

到目前为止,这一直很有效,直到我开始使用 不需要任何构造函数参数的条件类型。在这种情况下,Visual Studio 编译器会触发错误:

error C2512: 'blahblah::Not<blahblah::SomeConditionType>::Not' : no appropriate default constructor available

我尝试通过添加 Not 构造函数的一些条件或特化来解决此问题,但到目前为止没有任何效果。我没有看到简单的解决方案,但我对 enable_if 和相关构造的了解有限。

问题的根源是我确实需要在 Not 构造函数中使用可变参数模板参数来将构造函数参数传递给实际条件。但是,当没有参数并且可以默认构造条件时,我的代码似乎无法生成一个空的可变参数模板;可能是因为 enable_if 生成了最后一个类型。 我在构造函数中使用的 enable_if 是为了确保复制构造始终调用 Not 复制构造函数,而不是将 Not to copy 转发给条件构造函数。

我找不到的是在这种情况下如何在 Not 中允许默认构造。

编辑>

这是一个完整的复制案例:

#include <type_traits>

template< class ConditionType >
class Not
{
    ConditionType m_condition;
public:
    using this_type = Not<ConditionType>;

    template< class ...Args
        , typename = std::enable_if< 
            !std::is_same< this_type, typename std::decay<Args>::type...>::value 
        >::type
    >
    Not( Args&&... args )
        : m_condition( std::forward<Args>(args)... ) {}


    Not( const Not& ) = default;
    Not& operator=( const Not& ) = default;

    template< class ...Args >
    bool operator()( Args&&... args ) const
    {
        return ! m_condition( std::forward<Args>(args)... );
    }

    friend inline bool operator==( const Not& left, const Not& right )
    {
        return left.m_condition == right.m_condition;
    }

    friend inline bool operator<( const Not& left, const Not& right )
    {
        return left.m_condition < right.m_condition;
    }

};

class ConditionThatCompile
{
    int m_key;
public:
    ConditionThatCompile( int key ) : m_key( key ) {}

    bool operator()( const int& value ) const
    { 
        return m_key > value;
    } 
    friend inline bool operator==( const ConditionThatCompile& left, const ConditionThatCompile& right ) 
    { 
        return left.m_key == right.m_key; 
    } 
    friend inline bool operator<( const ConditionThatCompile& left, const ConditionThatCompile& right ) 
    { 
        return left.m_key < right.m_key;
    } 
};

class ConditionThatDoNotCompile
{
public:
    bool operator()( const int& value ) const
    { 
        return true;
    } 
    friend inline bool operator==( const ConditionThatDoNotCompile& left, const ConditionThatDoNotCompile& right ) 
    { 
        return true; 
    } 
    friend inline bool operator<( const ConditionThatDoNotCompile& left, const ConditionThatDoNotCompile& right ) 
    { 
        return false;
    } 
};

void do_something();

void somewhere()
{
    Not<ConditionThatCompile> compiling_condition { 42 };

    if( compiling_condition( 100 ) ) 
        do_something();

    Not<ConditionThatDoNotCompile> not_compiling_condition;
};

【问题讨论】:

  • 嗯...如果您可以创建一个小的、可编译的示例来显示问题,它可能会有所帮助。
  • 你不能实现默认构造函数 Not(void){}?
  • Not() = default;添加到类并相应地编写另一个构造函数。
  • 似乎std::is_same&lt; this_type, typename std::decay&lt;Args&gt;::type...&gt;::value 在没有参数的情况下无法匹配,因为基本上Args 是“空”然后(另外:这段代码是否适用于多个参数?看起来这种情况不'也不匹配模板)。
  • @Rubens 应该是 Not(){} 我不知道为什么 C++ 标准仍然支持 C 语法。更糟糕的是,VS 模板使用它...

标签: c++ c++11 visual-studio-2013


【解决方案1】:

最简单的方法是派生和继承所有构造函数:

template<typename F>
struct Not : private F
{
    using F::F;

    template<typename... A>
    bool operator()(A&&... a) const { return !F::operator()(std::forward<A>(a)...); }
};

如果你的编译器不支持继承基构造函数,你确实需要定义一个构造函数模板,尽管我之前的理解,这个模板似乎确实与隐式定义的复制/移动构造函数混淆了。这种情况下的解决方案是

template<typename F>
class Not : F
{
    template<typename... A> struct ok            : std::true_type {};
    template<typename... A> struct ok<Not, A...> : std::false_type {};

public:
    template<typename... A, typename = typename std::enable_if<
        ok<typename std::decay<A>::type...>{}
    >::type>
    Not(A&&... a) : F{std::forward<A>(a)...} {}

    template<typename... A>
    bool operator()(A&&... a) const { return !F::operator()(std::forward<A>(a)...); }
};

这有点笨拙,但是您对std::is_same 的使用不正确。如果这对你有用,最好以更简洁的方式写ok,包括在课堂外使用decay,以便更一般地使用,并给它一个更好的名字。这样就不会那么笨拙了。

live example 表明这两种替代方案都适用于某些扩展测试。

【讨论】:

  • 不应该是私有继承吗?由于 ConditionType 主题在问题中是私有的,因此也不需要隐式转换。
  • @BenVoigt 很可能是的。已更正并将 ok 设为私有。谢谢。
【解决方案2】:

好的,现在我看到您的问题涉及许多转换场景,其中编译器会找到模板化构造函数,因为它不是专门寻找复制构造函数。

你说

我在构造函数中使用的enable_if 是为了确保复制构造始终调用Not 复制构造函数,而不是将Not 转发到条件构造函数。

这完全没有必要。模板构造函数不能用于复制对象。

您甚至不需要显式默认复制构造函数和复制赋值运算符,因为编译器会生成隐式默认声明。

去掉enable_if,听起来默认构造也应该开始工作了。

有关使用模板构造函数复制对象的更多信息,请参阅template copy constructor

【讨论】:

  • 好吧,我摆脱了它并得到了预期的错误:当我尝试 Not 的复制构造时,构造函数参数被传递给成员构造函数,这会触发错误,因为组件可以't 取一个 Not,这表明它不是被调用的复制构造函数。当然这可能是编译器实现的问题,但我也记得 Scott Meyers 曾谈到过这个问题……
  • @Klaim:我猜你正在使用一些实际上并没有复制的代码?你能举一个在这里失败的(不是实际复制的)代码的例子吗?
  • “模板构造函数不能用于复制对象。”。它用于复制如果默认生成的需要转换(从非常量到常量):1. Demo : Requires Conversion, 2. Demo: Requires NO Conversion
  • 这是我刚刚再次测试的确切代码:gist.github.com/Klaim/e9da89779310c7fa5ef8
  • @BenVoigt:它在功能上是相同的(通常)。您为该问题选择了一个不同的名称,这在迂腐上是正确的,但实际上存在一个问题。当我写A a(b)时,我希望调用复制构造函数,但它没有,问题就在这里。
猜你喜欢
  • 2018-05-15
  • 1970-01-01
  • 2012-04-20
  • 2014-03-24
  • 1970-01-01
  • 2021-04-02
  • 1970-01-01
  • 2018-03-11
  • 1970-01-01
相关资源
最近更新 更多